发布于 2026-01-06 7 阅读
0

使用 Cypress 进行 API 测试:第二部分 - 创建测试用例 大家好! HTTP 方法 用户 端点 GET 第一个测试用例 第二个测试用例 cypress.json - baseUrl POST cypress-api-tutorial

使用 Cypress 进行 API 测试:第 2 部分 - 创建测试

大家好!

HTTP 方法

用户端点

得到

第一个测试用例

第二个测试用例

cypress.json - baseUrl

邮政

cypress-api-教程

大家好!

接下来,让我们继续讨论上一篇文章中提到的主题,重点关注对我们来说最重要的部分——测试

HTTP 方法

基于微服务的应用程序的资源可以通过多种方式进行操作。可以创建、更新、删除资源,以及执行其他操作。

当我们发送服务请求时,需要通过 URI 来告知服务请求方我们要处理的资源。我们还需要告知服务请求方我们要对资源执行的操作类型。为此,我们将使用 HTTP 协议方法。

HTTP 协议有多种方法,每种方法的功能各不相同。最常用的方法有:

  • GET:检索资源的数据。
  • POST:创建新资源。
  • PUT:对已存在的特定资源进行更改。
  • PATCH:对给定资源进行部分更新。
  • 删除:删除特定资源。

用户端点

回到我们的目标 API—— ServeRest,让我们首先测试用户的端点:

替代文字

目标

  1. 使用 GET 方法/usuarios检索所有用户数据。
  2. 验证查询参数。
  3. 验证状态码。
  4. 验证响应正文的内容。

好了,都给我动起来!开始吧!

我们将从 Integration 文件夹中删除上一篇文章中的所有内容。我们的测试代码应该放在那里,所以我们只保留必要的代码。

你可以手动删除,或者如果你像我一样懒,可以使用下面的命令:



rm -rf cypress/integration/*


Enter fullscreen mode Exit fullscreen mode

现在我们将创建一个名为 `<user_endpoint>` 的文件夹Usuarios,用于存储所有与用户端点相关的请求。这将有助于我们保持代码的条理性和组织性。

在文件夹内Usuarios,我们将创建我们的第一个测试规范,名为GETusuarios.spec.js

现在项目结构应该如下所示:



├── fixtures
├── integration
│   └── Usuarios
│       └── GETUsuarios.spec.js
├── plugins
│   └── index.js
├── support
│   ├── commands.js
│   └── index.js
└── videos


Enter fullscreen mode Exit fullscreen mode

得到

GETUsuarios.spec.js

让我们添加 Mocha 的基本组织结构。如果需要,您可以使用 BDD 风格:

  • 描述- Given->测试套件名称
  • 上下文- When->测试内部套件名称
  • ——Then应该把你的测试放在这里!


/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send GET /usuarios', () => {
    it('Then it should return a list with all registered users', () => {
      // place your tests here
    });
  });

  context('When I send GET /usuarios passing id query param', () => {
    it('Then it should return only the filtered user', () => {
      // place your tests here 
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

第一个测试用例

现在让我们使用 Cypress 方法cy.request发出 HTTP 请求:

  • 方法 - GET
  • URL - API 地址 + 端点!```javascript

cy.request({
method: 'GET',
url: ' https://serverest.dev/usuarios '
})


After that we will call the `.should` function, which I mentioned in the previous post. It will enable us to make multiple assertions on the yielded subject - **response** in this case.

```javascript


.should((response) => {
  // all your assertions should be placed here!!
});


Enter fullscreen mode Exit fullscreen mode

让我们添加一条日志,看看“响应”在正文中返回了什么:



cy.log(JSON.stringify(response.body))


Enter fullscreen mode Exit fullscreen mode

简而言之,cy.log它将访问响应的“body”属性。该JSON.stringify函数将用于把响应体转换为字符串。

替代文字

运行该命令cypress:open并查看日志返回的内容。



npm run cypress:open


Enter fullscreen mode Exit fullscreen mode

替代文字

太棒了!我们可以得出结论,我们的调用工作正常,因为我们正确收到了响应正文和 200 状态码(成功)。

让我们移除cy.log(我们不希望测试中出现垃圾数据),并添加一些状态码和响应体断言。

我们可以先验证最简单的那个,即状态码



expect(response.status).to.eq(200)


Enter fullscreen mode Exit fullscreen mode

太好了!上面的代码表示我们期望响应的状态码等于 200。

我们可以断言,quantidade (数量)键始终与usuarios (用户)数组中的元素个数相同。让我们添加以下验证:



expect(response.body.quantidade).to.eq(response.body.usuarios.length)


Enter fullscreen mode Exit fullscreen mode

让我们验证一下“email发件人”字段usuarios是否不能为空……

替代文字

嗯,但是usuarios这是一个包含三个元素的列表。那我们该怎么做呢?

我们需要传递要usuarios访问的列表索引才能做到这一点。现在,让我们访问列表中的第一个对象,传递 'usuarios[0]':



expect(response.body.usuarios[0].email).to.not.be.null


Enter fullscreen mode Exit fullscreen mode

替代文字

太好了,成功了!但是我们如何确保usuarios数组中所有对象的“email”键都不为空呢?

或许可以复制上一行,只需更改索引即可?



expect(response.body.usuarios[0].email).to.not.be.null
expect(response.body.usuarios[1].email).to.not.be.null


Enter fullscreen mode Exit fullscreen mode

是的,这或许可行。但是,如果这个数组里有上千个用户呢?难道我们要在代码里增加上千行吗?

我不这么认为。

为了进行更智能的断言,我们可以使用Cypress loadash,它为我们提供了以下.each()函数:



Cypress._.each(response.body.usuarios, (usuario) => {
  expect(usuario.email).to.not.be.null
})


Enter fullscreen mode Exit fullscreen mode

这个函数几乎就像 forEach() 一样工作,遍历数组并对 'usuarios' 数组中的每个 'usuario' 对象进行断言。

替代文字

让我们借此机会添加最后一个验证usuarios。我们期望每个对象都包含所有键('nome'、'email'、'password'、'administrador'、'_id'):



expect(usuario).to.have.all.keys('nome', 'email', 'password', 'administrador', '_id')


Enter fullscreen mode Exit fullscreen mode

替代文字

第二个测试用例

接下来,我们将发送与之前相同的请求,但这次会传递一个参数,query string以便仅按以下条件筛选出一个用户_id

替代文字

添加验证以确保名称始终正确:



context('When I send GET /usuarios passing id query param', () => {
  it('Then it should return only the filtered user', () => {
    cy.request({
      method: 'GET',
      url: 'https://serverest.dev/usuarios',
      qs: {
        _id: '0uxuPY0cbmQhpEz1'
      }
    })
      .should((response) => {
        expect(response.status).to.eq(200)
        expect(response.body.usuarios[0].nome).to.eq("Fulano da Silva")
      });
  });
});


Enter fullscreen mode Exit fullscreen mode

替代文字

cypress.json - baseUrl

我们url在两个文件中都重复使用了该参数cy.request()。请将以下几行添加到您的cypress.json文件中,这样就无需重复输入这些信息了。

也设置videofalse了。我们不想让 Cypress 帮我们录制。



{
  "baseUrl": "https://serverest.dev",
  "video": false
}


Enter fullscreen mode Exit fullscreen mode

好的,这算是一个好的开始,现在我们的代码看起来是这样的:



/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send GET /usuarios', () => {
    it('Then it should return a list with all registered users', () => {
      cy.request({
        method: 'GET',
        url: '/usuarios'
      })
        .should((response) => {
          expect(response.status).to.eq(200)
          expect(response.body.quantidade).to.eq(response.body.usuarios.length)
          Cypress._.each(response.body.usuarios, (usuario) => {
            expect(usuario.email).to.not.be.null
            expect(usuario).to.have.all.keys('nome', 'email', 'password', 'administrador', '_id')
          })
        });
    });
  });

  context('When I send GET /usuarios passing id query param', () => {
    it('Then it should return only the filtered user', () => {
      cy.request({
        method: 'GET',
        url: '/usuarios',
        qs: {
          _id: '0uxuPY0cbmQhpEz1'
        }
      })
        .should((response) => {
          expect(response.status).to.eq(200)
          expect(response.body.usuarios[0].nome).to.eq("Fulano da Silva")
        });
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

请注意,我们向同一个端点发送了两个请求。这需要重构,但我们稍后再讨论这个问题。

邮政

POSTUsuarios.spec.js

接下来,我们将介绍下一个 HTTP 方法。为此,我们将创建一个名为 POSTUsuarios.spec.js 的新文件。我们将把所有与 POST 方法相关的测试都放在这个文件中。

使用 Mocha 函数创建测试结构,就像我们在 GET 文件中所做的那样。显然,需要根据场景修改描述describecontextit



/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

这次的cy.request()功能会略有不同。

  • 该方法将是POST……
  • URL 将保持不变/users(无需添加完整的 URL,只需添加资源即可)。
  • body参数中,我们将添加创建新用户所需的信息。要了解需要哪些信息,请务必查阅ServeRest的 API 文档。

替代文字

我们的有效载荷将如下所示:



body: {
  nome: "Dumb John",
  email: "dumb.john@qa.com.br",
  password: "test",
  administrador: "true"
}


Enter fullscreen mode Exit fullscreen mode

让我们借此机会验证一下成功消息和状态码。

替代文字



/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
      cy.request({
        method: 'POST',
        url: '/usuarios',
        body: {
          nome: "Dumb Joe",
          email: "dumb.joe@qa.com.br",
          password: "test",
          administrador: "true"
        }
      })
        .should((response) => {
          expect(response.status).eq(201)
          expect(response.body.message).eq("Cadastro realizado com sucesso")
        });
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

替代文字

成功!确定吗?请再运行一次测试。

替代文字

当我们再次运行测试时,它失败了,这是为什么呢?这是因为根据业务规则,不允许使用已使用的电子邮件地址注册用户。让我们使用一种策略来解决这个问题。

让我们编辑位于以下位置的文件:plugins > index.js

我们将使用 libFaker为每个请求创建一个不同的用户。faker使用以下npm命令安装:



npm i -D faker


Enter fullscreen mode Exit fullscreen mode

我将其命名taskfreshUser(),在它里面我们将使用fakerlib 传递键/值:



freshUser() {
  user = {
    nome: faker.name.firstName(),
    email: faker.internet.email(),
    password: faker.internet.password(),
    administrador: "true"
  };
  return user;
}


Enter fullscreen mode Exit fullscreen mode

完整文件如下所示:



/// <reference types="cypress" />

const faker = require("faker");

/**
 * @type {Cypress.PluginConfig}
 */

module.exports = (on, config) => {
  on("task", {
    freshUser() {
      user = {
        nome: faker.name.firstName(),
        email: faker.internet.email(),
        password: faker.internet.password(),
        administrador: "true"
      };
      return user;
    }
  })
  return config
}


Enter fullscreen mode Exit fullscreen mode

现在重构POSTUsuarios.spec.js以使用faker。创建一个名为的变量fakeUser



let fakeUser;


Enter fullscreen mode Exit fullscreen mode

为了在每次执行之前创建不同的有效载荷,我们将使用一个名为 `.` 的钩子。在这个钩子中,我们将调用由任务创建的抛出beforeEach()函数,并将结果传递给变量。cy.task()userfakeUser



beforeEach(() => {
  cy.task('freshUser').then((user) => {
    fakeUser = user;
    cy.log(JSON.stringify(fakeUser))
  });
});


Enter fullscreen mode Exit fullscreen mode

我还添加了一个功能,cy.log()用于查看每个请求创建的用户。请从您的代码中删除此部分

将主体参数更改为接收fakeUser变量。



cy.request({
  method: 'POST',
  url: '/usuarios',
  body: fakeUser
})


Enter fullscreen mode Exit fullscreen mode

重构后的文件如下所示:



/// <reference types="cypress" />

let fakeUser;

describe('Given the Users api', () => {
  beforeEach(() => {
    cy.task('freshUser').then((user) => {
      fakeUser = user;
      cy.log(JSON.stringify(fakeUser))
    });
  });

  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
      cy.request({
        method: 'POST',
        url: '/usuarios',
        body: fakeUser
      })
        .should((response) => {
          expect(response.status).eq(201)
          expect(response.body.message).eq("Cadastro realizado com sucesso")
        });
    });
  });
});


Enter fullscreen mode Exit fullscreen mode

现在我们可以根据需要运行任意次数的测试。

替代文字

我就先写完这篇文章了,因为这里已经有很多信息了!

请记住,仅仅阅读这篇文章并不能让你学会。你需要自己编写代码、练习、失败,直到成功为止!

请在 GitHub 上查看此项目仓库

GitHub 标志 murillowelsi / cypress-api-tutorial

文章《使用 Cypress 进行 REST API 测试》中创建的存储库

cypress-api-教程

文章《使用 Cypress 进行 REST API 测试》中创建的存储库






欢迎在LinkedIn上加我好友。请留下您的评论、问题和建议。

感谢您的关注,我们下篇文章再见!

文章来源:https://dev.to/murillowelsi/api-testing-with-cypress-part-2-creating-your-tests-270i