使用 Cypress 测试 API

使用 Cypress 测试 API

Cypress 可以直接测试 REST 和 GraphQL API,无需进行浏览器导航,也不需要另一套工具。cy.request() 会发送真实 HTTP 请求并产出响应,让你在运行 UI 测试的同一组 spec、同一份配置和同一个 CI 作业里,对状态码、响应体、响应头及耗时进行断言。

因此,Cypress 适合测试认证流程、增删改查、校验错误和分页,也适合通过 HTTP 准备应用状态,免去为了得到某种状态而不断点击表单。本指南介绍第一个 API 测试的写法、cy.request() 的工作原理,以及认证、错误响应、文件上传、GraphQL、轮询、录制 fixture 和结合 API 与 UI 测试等常见模式。

为什么用 Cypress 测试 API

多数团队已经有一套 Cypress UI 测试。把 API 测试加入其中,就可以共用一个运行器、一份配置、一个 CI 作业,并在出错时集中排查。具体好处包括:

  • 更快获得接口契约的反馈。API 测试失败说明后端发生了变化;UI 测试失败则可能是后端变化、选择器位置变化,或者竞态条件。直接测试端点可减少这种歧义。
  • 初始化与清理比点击更快。通过 HTTP 创建用户、准备订单或登录只需毫秒,通过 UI 完成相同步骤则需要数秒,而且会重复测试其他地方已经覆盖的代码路径。
  • 覆盖 UI 无法触及的后端情况。校验错误、权限边界、限流以及分页边界通常难以从表单触发,却很容易用请求触发。
  • 本地与 CI 中都可查看真实细节。每个请求都会在 Command Log 中显示方法、状态码和 URL;点击记录后,完整请求与响应会输出到浏览器控制台。这既适用于交互模式,也可以通过 Test Replay 检查已经结束的 CI 运行。
  • 测试可以跨越两个层次。API 调用与 UI 命令位于同一条命令链中,因此可以用 HTTP 准备状态,通过界面操作应用,再用 HTTP 确认后端确实持久化了修改。

Cypress 在端到端测试类型下运行 API 测试。最常用的命令是 cy.request(),它发送真实 HTTP 请求并返回响应。

编写第一个 API 测试

设置 baseUrl(可选)

可以直接向 cy.request() 传入完整 URL 开始测试,因此这一步不是必需的。设置 baseUrl 后,每个请求都能使用相对路径,只需改变一个值,就能让同一组 spec 指向另一套环境。

cypress.config.js

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3001',
  },
})

cypress.config.ts

import { defineConfig } from 'cypress'

export default defineConfig({
  e2e: {
    baseUrl: 'http://localhost:3001',
  },
})

cypress/e2e/api/users.cy.js

// with a baseUrl set
cy.request('/users')

// without one, pass the full URL
cy.request('http://localhost:3001/users')

创建 spec

cypress/e2e/api/users.cy.js

describe('GET /users', () => {
  it('returns a list of users', () => {
    cy.request('GET', '/users').then((response) => {
      expect(response.status).to.eq(200)
      expect(response.body.results).to.have.length.greaterThan(1)
    })
  })
})

运行测试

npm

npx cypress run --spec 'cypress/e2e/api/users.cy.js'

Yarn

yarn cypress run --spec 'cypress/e2e/api/users.cy.js'

pnpm

pnpm cypress run --spec 'cypress/e2e/api/users.cy.js'

Bun

bunx cypress run --spec 'cypress/e2e/api/users.cy.js'

这就是一个完整的 API 测试。不必先访问页面,也不会发生浏览器导航。cy.request() 会基于 baseUrl 解析相对路径 /users,然后产出响应。

响应对象的组成

cy.request() 产出一个响应对象。以下是断言中最常用的属性:

属性 说明
status HTTP 状态码,例如 200。
statusText 状态文本,例如 'OK'。
body 响应体;响应 Content-Type 以 json 结尾时,会解析为对象。
headers 响应头。
duration 请求耗时,单位为毫秒。
isOkStatusCode 对于 2xx 和 3xx 响应,其值为 true。
requestHeaders Cypress 实际发送的请求头,包括自动附加的 cookie。
requestBody Cypress 实际发送的请求体。
redirects 发生并跟随重定向时存在,值为类似 ['301: http://localhost:3001/new'] 的数组。
allRequestResponses 整条链中的所有请求与响应,跟随重定向时尤其有用。

响应体的解析由响应决定,而非请求。如果服务器返回 Content-Type: application/json,response.body 就是对象。否则它是字符串,即使发送的是 JSON 请求体也一样。

需要多项检查时,可以在 .then() 中断言;只关心一个值时,也可以直接链式断言:

cypress/e2e/api/users.cy.js

// several assertions against one response
cy.request('/users/1').then((response) => {
  expect(response.status).to.eq(200)
  expect(response.body).to.have.property('email')
  expect(response.duration).to.be.lessThan(1000)
})

// a single value, read as a sentence
cy.request('/users/1').its('body.username').should('eq', 'jdoe')

// implicit assertion on the whole body
cy.request('/users/1').its('body').should('deep.include', {
  id: 1,
  role: 'admin',
})

cy.request() 如何工作

理解底层机制,可以避免大多数令人意外的行为。

调用 cy.request() 时,驱动通过 WebSocket 连接把选项交给 Cypress 的 Node 进程,再由 Node 发起 HTTP 请求。浏览器不会负责发送这个请求。

这一点解释了下面的行为:

行为 原因
请求不会出现在浏览器 DevTools 的 Network 标签页。 它不是浏览器请求,浏览器无内容可报告。
cy.intercept() 无法监视或模拟它。 拦截针对经过 Cypress 代理的浏览器流量,而此请求不会进入该代理。
不受 CORS 或同源策略限制。 这些规则由浏览器实施,而这里没有浏览器参与,可以向任意主机发起请求。
cookie 仍然使用真实浏览器的 cookie 存储。 参见与浏览器共享 cookie。
User-Agent 与被测浏览器一致。 Cypress 转发浏览器的用户代理标识,让服务器看到一致的客户端。
无效或自签名 TLS 证书不会导致请求失败。 证书验证要求较宽松,便于向预发布环境发送请求。

还需要了解几个默认行为:

  • 非 2xx/3xx 响应会使测试失败。failOnStatusCode 默认为 true。如果错误响应正是测试目标,应将它设为 false。
  • 默认跟随重定向。followRedirect 默认为 true;设为 false 后,可以通过 response.redirectedToUrl 对 Location 指向的位置作断言。
  • 自动序列化对象请求体。body 是对象或布尔值时,Cypress 会进行 JSON 序列化,并设置 Content-Type: application/json。字符串请求体则原样发送,不自动补充内容类型。
  • 自动重试短暂网络错误。retryOnNetworkFailure 默认为 true,最多重试4次。状态码错误默认不重试,除非启用 retryOnStatusCodeFailure。
  • 超时使用 responseTimeout。它不使用 defaultCommandTimeout。可以通过单个请求的 timeout 选项覆盖。

与浏览器共享 cookie

cy.request() 不维护独立的 cookie 存储。发送前,Cypress 会向浏览器索取与请求 URL 匹配的 cookie 并附加到请求上。如果响应包含 Set-Cookie,Cypress 会将 cookie 写入浏览器,遵守过期时间,并删除服务器要求清除的 cookie。

这种双向共享让混合测试成为可能:

  • 通过 HTTP 登录,然后继续操作 UI。向登录端点发送 cy.request() 后,会话 cookie 留在浏览器中,后续 cy.visit() 就能打开已登录页面,无需填写表单或等待重定向。
  • 通过 UI 登录,然后继续调用 HTTP。点击真实登录表单建立的会话会用于后续每个 cy.request(),因此无需重新认证或手工构造 token,就能对受保护端点进行断言。
  • 验证退出登录的真实效果。服务器通过 Set-Cookie 清除 cookie 的操作会落到真实浏览器存储中,可以先调用退出端点,再断言 UI 已把用户视为未登录。

独立 API 测试工具与浏览器各自运行,工具的 cookie 和应用的 cookie 相互分离。要打通它们,往往需要导出 token 再重新注入,这时测试的不仅是应用,还有连接两者的辅助机制。

常见场景

测试完整的 CRUD 生命周期

Cypress 命令可以串成链,因此创建、读取、更新、删除流程能按从上到下的顺序表达。从创建响应中取出 ID,并传给后续步骤即可。

cypress/e2e/api/articles.cy.js

describe('/articles', () => {
  it('creates, reads, updates, and deletes an article', () => {
    cy.request('POST', '/articles', {
      title: 'Testing APIs with Cypress',
      status: 'draft',
    })
      .then((response) => {
        expect(response.status).to.eq(201)
        expect(response.body).to.include({ status: 'draft' })

        return response.body.id
      })
      .then((articleId) => {
        cy.request(`/articles/${articleId}`)
          .its('body.title')
          .should('eq', 'Testing APIs with Cypress')

        cy.request('PATCH', `/articles/${articleId}`, { status: 'published' })
          .its('body.status')
          .should('eq', 'published')

        cy.request('DELETE', `/articles/${articleId}`)
          .its('status')
          .should('eq', 204)

        cy.request({
          url: `/articles/${articleId}`,
          failOnStatusCode: false,
        })
          .its('status')
          .should('eq', 404)
      })
  })
})

认证一次,复用会话

通过 HTTP 登录是快速获得已认证测试环境的方法。用 cy.session() 包装它,就能缓存并恢复结果,而不必在每个测试中重新登录。

API 使用 cookie 认证时,cy.request() 会自动使用相应 cookie,因为 Cypress 在每次请求前都会读取浏览器的 cookie 存储。

cypress/support/commands.js

Cypress.Commands.add('loginByApi', (username, password) => {
  cy.session(
    ['loginByApi', username],
    () => {
      cy.request('POST', '/auth/login', { username, password })
        .its('status')
        .should('eq', 200)
    },
    {
      cacheAcrossSpecs: true,
      validate() {
        cy.request('/auth/me').its('status').should('eq', 200)
      },
    }
  )
})

如果 API 使用 Bearer token,应在登录时存储 token,并在请求中显式发送。cy.session() 会与 cookie 一起缓存 localStorage,因此恢复会话时 token 也会保留。

cypress/support/commands.js

const authToken = () => window.localStorage.getItem('authToken')

Cypress.Commands.add('loginByApi', (username, password) => {
  cy.session(
    ['loginByApi', username],
    () => {
      cy.request('POST', '/auth/login', { username, password }).then(
        (response) => {
          expect(response.status).to.eq(200)
          window.localStorage.setItem('authToken', response.body.token)
        }
      )
    },
    {
      cacheAcrossSpecs: true,
      validate() {
        cy.request({
          url: '/auth/me',
          headers: { authorization: `Bearer ${authToken()}` },
        })
          .its('status')
          .should('eq', 200)
      },
    }
  )
})

凭据本身应放在环境变量中,不能写入 spec。通过 cy.env() 读取,避免环境凭据被整体注入浏览器,并使其由 Node 进程管理。

cypress/e2e/api/orders.cy.js

describe('GET /orders', () => {
  beforeEach(() => {
    cy.env(['username', 'password']).then(({ username, password }) => {
      cy.loginByApi(username, password)
    })
  })

  it('returns orders belonging only to the signed-in customer', () => {
    cy.env(['customerId']).then(({ customerId }) => {
      cy.request({
        url: '/orders',
        headers: { authorization: `Bearer ${authToken()}` },
      })
        .its('body.orders')
        .should('have.length.greaterThan', 0)
        .each((order) => {
          expect(order.customerId).to.eq(customerId)
        })
    })
  })
})

对错误响应进行断言

错误路径通常是 API 中测试最少的部分,但在这一层覆盖它的成本很低。设置 failOnStatusCode: false,让 Cypress 产出响应,而非直接使测试失败。

cypress/e2e/api/validation.cy.js

describe('POST /orders validation', () => {
  it('rejects an order with no line items', () => {
    cy.request({
      method: 'POST',
      url: '/orders',
      body: { lineItems: [] },
      failOnStatusCode: false,
    }).then((response) => {
      expect(response.status).to.eq(422)
      expect(response.body.errors).to.deep.include({
        field: 'lineItems',
        message: 'must contain at least one item',
      })
    })
  })

  it('refuses an unauthenticated request', () => {
    cy.request({
      url: '/orders',
      headers: { authorization: '' },
      failOnStatusCode: false,
    })
      .its('status')
      .should('eq', 401)
  })

  it('hides an order belonging to another customer behind a 404', () => {
    cy.request({
      url: '/orders/does-not-belong-to-me',
      failOnStatusCode: false,
    }).then((response) => {
      expect(response.status).to.eq(404)
      expect(response.body).to.not.have.property('customerId')
    })
  })
})

发送查询参数并遍历分页

qs 选项会把对象合并到 URL 的查询字符串中,无需自己拼接和编码。

cypress/e2e/api/search.cy.js

it('paginates search results', () => {
  cy.request({
    url: '/products/search',
    qs: { q: 'wireless keyboard', page: 1, perPage: 25 },
  }).then((response) => {
    expect(response.status).to.eq(200)
    expect(response.body.items).to.have.length(25)
    expect(response.body.page).to.eq(1)

    cy.request({
      url: '/products/search',
      qs: { q: 'wireless keyboard', page: 2, perPage: 25 },
    }).then((secondPage) => {
      const firstIds = response.body.items.map((item) => item.id)
      const secondIds = secondPage.body.items.map((item) => item.id)

      expect(secondIds).to.not.have.members(firstIds)
    })
  })
})

验证响应结构

对值进行断言,可以发现数据错误;对结构进行断言,可以发现接口契约被破坏,而这类变化最容易悄无声息地进入生产环境。

cypress/e2e/api/contract.cy.js

it('returns every field the checkout page depends on', () => {
  cy.request('/cart')
    .its('body')
    .then((cart) => {
      expect(cart).to.have.all.keys(
        'id',
        'items',
        'subtotal',
        'tax',
        'total',
        'currency'
      )

      expect(cart.currency).to.be.oneOf(['USD', 'EUR', 'GBP'])
      expect(cart.total).to.be.a('number')

      cart.items.forEach((item) => {
        expect(item).to.include.all.keys('sku', 'quantity', 'unitPrice')
        expect(item.quantity).to.be.greaterThan(0)
      })
    })
})

上传文件

将 FormData 实例作为请求体传入。Cypress 会编码该内容,并设置带有正确 boundary 的 multipart/form-data 内容类型。

cypress/e2e/api/uploads.cy.js

it('accepts an avatar upload', () => {
  // image fixtures are read as base64 by default
  cy.fixture('avatar.png').then((image) => {
    const formData = new FormData()

    formData.append(
      'file',
      Cypress.Blob.base64StringToBlob(image, 'image/png'),
      'avatar.png'
    )
    formData.append('visibility', 'public')

    cy.request({
      method: 'POST',
      url: '/users/me/avatar',
      body: formData,
    }).then((response) => {
      expect(response.status).to.eq(201)
      expect(response.body.url).to.match(/avatar\.png$/)
    })
  })
})

查询 GraphQL API

GraphQL 使用接收 JSON 请求体的单一端点,因此不需要特殊处理。应断言 body.data,同时确认不存在 body.errors,因为查询发生错误时,GraphQL 也可能返回 200。

cypress/e2e/api/graphql.cy.js

const gql = (query, variables) =>
  cy.request('POST', '/graphql', { query, variables })

it('fetches a user with their recent orders', () => {
  gql(
    `query User($id: ID!) {
      user(id: $id) {
        id
        email
        orders(last: 3) { id total }
      }
    }`,
    { id: '1' }
  ).then((response) => {
    expect(response.status).to.eq(200)
    expect(response.body).to.not.have.property('errors')
    expect(response.body.data.user.email).to.eq('jdoe@example.com')
    expect(response.body.data.user.orders).to.have.length.of.at.most(3)
  })
})

轮询异步任务

端点启动后台工作后,可以轮询直到任务完成。使用递归函数,能够限制重试次数,并在失败时给出清楚的错误消息。

cypress/e2e/api/exports.cy.js

const waitForExport = (exportId, attemptsLeft = 10) => {
  if (attemptsLeft === 0) {
    throw new Error(`Export ${exportId} never completed`)
  }

  return cy.request(`/exports/${exportId}`).then((response) => {
    if (response.body.status === 'complete') {
      return response.body
    }

    cy.wait(1000)

    return waitForExport(exportId, attemptsLeft - 1)
  })
}

it('generates a CSV export', () => {
  cy.request('POST', '/exports', { format: 'csv' })
    .its('body.id')
    .then((exportId) => waitForExport(exportId))
    .then((completed) => {
      expect(completed.downloadUrl).to.be.a('string')

      cy.request(completed.downloadUrl)
        .its('body')
        .should('contain', 'order_id,total,currency')
    })
})

把响应保存为 fixture

通过 cy.writeFile() 将真实响应写入磁盘,就能把实时端点的返回值变成可复用的测试数据。将文件放到 fixtures 目录后,就可以像手写 fixture 一样,通过 cy.fixture() 使用它。

cypress/e2e/api/record-fixtures.cy.js

it('records the product catalog for later use', () => {
  cy.request('/products').then((response) => {
    expect(response.status).to.eq(200)

    cy.writeFile('cypress/fixtures/products.json', response.body)
  })
})

收益体现在随后读取这些文件的测试中。UI 测试可使用录制的响应模拟同一个端点,让测试快速且可重复,同时使用服务器真实生成过的数据:

cypress/e2e/catalog.cy.js

it('renders every product in the catalog', () => {
  cy.intercept('GET', '/products', { fixture: 'products.json' }).as('products')

  cy.visit('/catalog')
  cy.wait('@products')

  cy.fixture('products').then((products) => {
    cy.get('[data-cy="product-card"]').should('have.length', products.length)
  })
})

不必保存整个响应。也可以只提取后续测试所需的值:

cypress/e2e/api/record-fixtures.cy.js

cy.request('/users?role=admin').then((response) => {
  cy.writeFile('cypress/fixtures/admin.json', {
    id: response.body.results[0].id,
    email: response.body.results[0].email,
    capturedAt: new Date().toISOString(),
  })
})

结合 API 调用与 UI 测试

这一模式依赖请求和浏览器共享会话,是独立 API 测试工具无法直接提供的能力。先通过 HTTP 准备状态,再从界面操作应用,最后用 HTTP 确认修改已被持久化。

cypress/e2e/checkout.cy.js

describe('checkout', () => {
  beforeEach(() => {
    cy.env(['username', 'password']).then(({ username, password }) => {
      cy.loginByApi(username, password)
    })

    // set up the cart over HTTP instead of clicking through the catalog
    cy.request('POST', '/cart/items', { sku: 'KB-102', quantity: 1 })
  })

  it('places an order and persists it on the server', () => {
    cy.visit('/checkout')
    cy.get('[data-cy="place-order"]').click()
    cy.contains('Thanks for your order').should('be.visible')

    // the UI says it worked, now confirm the backend agrees
    cy.request('/orders')
      .its('body.orders.0')
      .should('include', { status: 'confirmed' })
      .its('lineItems.0.sku')
      .should('eq', 'KB-102')
  })
})

准备工作跳过了 UI。登录和填充购物车并不是这个测试要检查的行为,因此通过 HTTP 在毫秒级完成;点击操作仅用于真正被测的行为。从不关注初始化本身的测试中去掉 UI 初始化,通常是端到端套件中收益最大的单项提速措施。

断言超越了 UI 表象。“Thanks for your order”只能证明消息被渲染出来;cy.request('/orders') 则能证明订单真实存在。如果前端显示确认消息,却悄悄丢弃了请求,纯 UI 测试仍可能通过。

应用执行的许多操作不会在屏幕上留下痕迹,而这正是纯 UI 测试的盲区:

cypress/e2e/admin-audit.cy.js

it('records an audit entry when an admin changes a role', () => {
  cy.visit('/admin/users/42')
  cy.get('[data-cy="role"]').select('editor')
  cy.get('[data-cy="save"]').click()
  cy.contains('Saved').should('be.visible')

  // nothing on screen shows this, but it is the compliance requirement
  cy.request('/audit-log?subject=user:42')
    .its('body.entries.0')
    .should('include', {
      action: 'role.changed',
      from: 'viewer',
      to: 'editor',
    })
})

Webhook 入队、邮件记录、搜索索引更新、缓存失效、分析事件发送,都可以在触发这些操作的同一个测试里进行断言,无需离开 Cypress。

组织 API 测试套件

随着套件增长,以下约定有助于保持可读性:

  • 按资源而非 HTTP 动词组织 spec。cypress/e2e/api/users.cy.js 存放所有针对 /users 的测试。文件内部为每个端点(如 GET /users、POST /users)创建一个 describe 块,让失败信息指出出错端点。
  • 用自定义命令封装重复的请求配置。认证头、API 版本前缀和默认的 failOnStatusCode 应集中在一个自定义命令中,不必在每个测试重复。
  • 大型载荷放入 fixture。请求体适合放在 cypress/fixtures/ 中,再通过 cy.fixture() 加载。响应则可以反过来用 cy.writeFile() 录制成 fixture,参见把响应保存为 fixture。
  • 使用 cy.task() 在测试间重置状态。cy.request() 与 API 通信。如果需要直接访问数据库,应使用运行在 Node 中的 cy.task()。
  • 主机地址与凭据放入环境变量。不同环境的配置参见环境变量。
  • 为之后要使用的响应设置别名。使用 .as(),而不是赋值给 let 变量,让值可以跨命令使用。

cypress/support/commands.js

Cypress.Commands.add('api', (options) => {
  return cy.env(['apiToken']).then(({ apiToken }) => {
    return cy.request({
      ...options,
      url: `/api/v2${options.url}`,
      headers: {
        accept: 'application/json',
        authorization: `Bearer ${apiToken}`,
        ...options.headers,
      },
    })
  })
})

cypress/e2e/api/users.cy.js

cy.api({ url: '/users/me' }).its('body.role').should('eq', 'admin')

保持 API 套件的运行速度

Cypress 会为每个 spec 文件启动浏览器。启动成本按 spec 产生,而非按测试产生,因此应摊薄这项成本。

把多个测试放入同一个 spec。一个包含200个 API 测试的 spec 只承担一次启动成本;200个各含一个测试的 spec 则承担200次。UI 套件通常适合拆成较小 spec 以便并行,但 API 套件的取舍相反,这往往是决定其运行速度的最大因素。

按 spec 并行,而非在 spec 内部并行。Cypress Cloud 会把 spec 文件分发到不同机器。因此并发来自多个具有一定规模的 API spec,而不是把一个 spec 拆得更碎。采用前文所述的按资源分组方式,通常自然就能得到合适的粒度。

纯 API 套件可关闭测试隔离。测试之间,Cypress 会访问 about:blank 并清除所有域的 cookie 与存储,以重置浏览器。从不渲染页面的 spec 无法从这种重置中获益;testIsolation: false 可以跳过它。

cypress/e2e/api/users.cy.js

describe('/users', { testIsolation: false }, () => {
  before(() => {
    cy.env(['username', 'password']).then(({ username, password }) => {
      cy.loginByApi(username, password)
    })
  })

  it('lists users', () => {
    cy.request('/users').its('status').should('eq', 200)
  })

  it('reads a single user', () => {
    cy.request('/users/1').its('body.id').should('eq', 1)
  })
})

现在,这个块内的测试会共享 cookie,通常符合需求:在 before 钩子中认证一次,spec 内每个测试继承会话。但这也意味着前一个测试可能为下一个测试留下状态,因此仍应明确写出各测试自身所需的准备步骤。

完全跳过 cy.visit()。API 测试不需要页面。一旦 spec 访问页面,每次执行该步骤的测试都要支付导航、页面加载及等待网络空闲的成本。

调试 API 测试

请求表现异常时,Command Log 是最快的排查入口。

编写测试时

每个 cy.request() 都会显示为 METHOD STATUS URL,并通过颜色指示状态是否成功。

Cypress Command Log 中的 POST 200 请求记录,以及响应下方的断言

点击记录,Cypress 会把详细信息输出到浏览器控制台:发送的 URL、请求头和请求体,收到的状态码、响应头与响应体,以及命令产出的值。如果跟随了重定向,每一跳都会列出。

浏览器控制台中的完整请求、响应、状态与耗时信息

CI 运行结束后

同样的检查方法也适用于已经结束的运行。Test Replay 会录制每个测试的命令日志。在 Cypress Cloud 打开失败的运行,点击回放中的 cy.request() 命令,就能在浏览器控制台看到相同的请求和响应细节。点击之前先打开 DevTools,因为详细信息会输出在那里。

Test Replay 中选中的请求命令及控制台显示的请求、响应和耗时详情

这能解决 CI 中 API 失败往往难以排查的问题。无需在本地重新运行套件、添加额外日志来猜测服务器返回了什么,而是直接阅读失败运行中真实发生的请求与响应。

选择合适的工具

目的 使用
直接调用端点,并对响应断言 cy.request()
断言、等待或模拟应用发出的请求 cy.intercept()
在 Node 中执行代码,例如数据库查询或文件 I/O cy.task()

有关模拟和等待应用流量的完整说明,参见拦截网络请求。

另请参阅

来源:API testing in Cypress 原始文档,官方呈现页面。Cypress 文档团队与贡献者。本文为中文翻译,命令组件展开为可复制代码。Copyright (c) 2017 Cypress.io,采用 MIT License。

MIT License 原文
## MIT License

Copyright (c) 2017 Cypress.io https://cypress.io

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容