使用 Cypress 进行 API 测试 - 第一部分

2025-06-09

使用 Cypress 进行 API 测试 - 第一部分

你知道 Cypress 吗?它是一个著名的测试自动化框架,可以帮助开发人员和 QA 编写更快、更轻松、更可靠的测试。在本教程中,我们将学习 Cypress 是什么,如何在项目中安装它,以及如何自动化 API 测试。

概括

关于柏树

Cypress 是一款适用于现代 Web 的前端测试工具。它能让你编写更快、更轻松、更可靠的测试。

Cypress 执行示例

使用 Cypress,您可以自动执行不同类型的测试:

  • 端到端
  • 成分
  • 一体化
  • 单元

柏树的主要特征是:

  • 轻松调试
  • 跨浏览器测试
  • 网络流量控制
  • 截图、视频和测试重播

测试类型快速概览

让我们快速概述一下测试类型:

  • 端到端(E2E):测试整个应用程序或系统,重点模拟真实的用户场景。此类测试的主要目的是验证所有组件和集成是否按预期工作。
  • 集成测试:验证组件或系统之间的集成。
  • 组件测试:与集成测试类似,但侧重于测试更大的代码单元。验证应用程序较大部分的行为是否正确。
  • 单元测试:专注于单独测试各个组件或功能。

项目设置

开始本教程,让我们先创建项目。使用 Cypress 的要求是安装 Node 18.x 或 20.x 版本。要查看 Node 的版本,请打开终端并输入:

node --version 
Enter fullscreen mode Exit fullscreen mode

如果您有节点,请在项目文件夹中运行:

npm init -y 
Enter fullscreen mode Exit fullscreen mode

然后安装 Cypress:

npm install cypress --save-dev
Enter fullscreen mode Exit fullscreen mode

现在,我们需要打开 Cypress,要做到这一点,请运行:

npx cypress open
Enter fullscreen mode Exit fullscreen mode

打开启动板后,选择 e2e 测试并接受配置。之后,你的项目将如下所示:

|--cypress/
|  |-- downloads
|  |-- e2e
|  |-- fixtures
|  |-- support
|      |-- commands.js
|      |-- e2e.js
|-- node_modules
|-- cypress.config.js
|-- package.json
Enter fullscreen mode Exit fullscreen mode

自动化测试

在本教程中,我们将使用我在 Postman 文章中使用的相同测试用例和 API,您可以在此处查看

现在,让我们创建测试文件。我们将为每种方法分别创建一个文件,以自动化不同的方法。第一个是 GET 方法,在 e2e 文件夹中,我们将创建一个名为 的新文件GETMethod.cy.js


|-- e2e
|  |-- GETMethod.cy.js
Enter fullscreen mode Exit fullscreen mode

Cypress 测试的基本结构如下:

describe('Name of the feature', ()=>{
    it('Name of the test itself', ()=>{
        expect(true).to.equal(true)
    })
})
Enter fullscreen mode Exit fullscreen mode

我们如何制作 GET 方法,我们将我们的功能名称更改为Testing GET method并创建我们的第一个测试Search all books with success

为了在 Cypress 上发出请求,我们将使用命令,cy.request()并且我们需要向该命令告知以下内容:

  • 方法:GET、POST、PUT(如果我们不传递,cypress 将使用默认方法 - GET)
  • 网址
  • 主体 - 如果需要,我们将在测试中告知该方法,以便我们查看各个功能之间的差异。您可以在此处了解有关此命令的更多信息

了解了这一点,我们的第一个测试将如下所示:

describe('Testing GET method', ()=>{
    it('Search all books with success', ()=>{

        cy.request('GET','https://fakerestapi.azurewebsites.net/api/v1/Books').then((response)=>{
            expect(response.status).to.equal(200)
            response.body.forEach(element => {
              expect(element).to.have.property('id')
              expect(element).to.have.property('title')
              expect(element).to.have.property('description')
              expect(element).to.have.property('pageCount')
              expect(element).to.have.property('excerpt')
              expect(element).to.have.property('publishDate')
            });
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

使用与其他相同的逻辑,我们的测试功能将是:

describe('Testing GET method', ()=>{
    it('Search all books with success', ()=>{

        cy.request('GET','https://fakerestapi.azurewebsites.net/api/v1/Books').then((response)=>{
        expect(response.status).to.equal(200)
        response.body.forEach(element => {
          expect(element).to.have.property('id')
          expect(element).to.have.property('title')
          expect(element).to.have.property('description')
          expect(element).to.have.property('pageCount')
          expect(element).to.have.property('excerpt')
          expect(element).to.have.property('publishDate')
        });
        })
    })
     it('Search for a specific books with success', ()=>{
        cy.request('GET','https://fakerestapi.azurewebsites.net/api/v1/Books/10').then((response)=>{
          expect(response.status).to.equal(200)
          expect(response).to.have.property('id')
          expect(response).to.have.property('title')
          expect(response).to.have.property('description')
          expect(response).to.have.property('pageCount')
          expect(response).to.have.property('excerpt')
          expect(response).to.have.property('publishDate')
        })
      })
    it('Search for a specific books with invalid Id', ()=>{
        cy.request('GET','https://fakerestapi.azurewebsites.net/api/v1/Books/10AAA').then((response)=>{
          expect(response.status).to.equal(400)
        })
    }) 
})
Enter fullscreen mode Exit fullscreen mode

在 Cypress 上使用命令

在开始自动化其他方法之前,让我们先改进一下测试。我们可以使用 Cypress 的命令让测试更易于阅读。Cypress 上的自定义命令允许您创建和覆盖现有命令。对于我们的测试,我们可以创建两个不同的命令:

  • 负责执行所有请求 - GET、POST、DELETE、PUT
  • 一个用于验证 API 的契约

让我们从发出所有请求的命令开始。Cypress 上的所有命令都将添加到commands.js文件中,要添加命令,我们使用以下结构:

Cypress.Commands.add('NameOfTheCommand', (variables)=>{
    //Actions of this command 
})
Enter fullscreen mode Exit fullscreen mode

为了测试,我们将创建命令BaseRequest。我们知道,对于此命令,我们需要有url、方法和请求体(针对POST和PUT方法),并且为了应对糟糕的场景,我们将此命令的参数设置failOnStatusCode为true,因此它看起来像这样:

Cypress.Commands.add('BaseRequest', (method, baseUrl, bodyRequest='')=>{
    cy.request({
        'method': method,
        'url': baseUrl,
        'body': bodyRequest == ''? null : bodyRequest,
        'failOnStatusCode': false
    })
})
Enter fullscreen mode Exit fullscreen mode

对于负责验证 API 契约的方法,我们需要在其上接收请求的响应主体,因此它将是:

Cypress.Commands.add('ContractValidation', (response)=>{
    expect(response).to.have.property('id')
    expect(response).to.have.property('title')
    expect(response).to.have.property('description')
    expect(response).to.have.property('pageCount')
    expect(response).to.have.property('excerpt')
    expect(response).to.have.property('publishDate')
})
Enter fullscreen mode Exit fullscreen mode

现在,我们可以更新我们的测试:

describe('Testing GET method', ()=>{
    const baseUrl = "https://fakerestapi.azurewebsites.net/api/v1/Books"
    it('Search all books with success', ()=>{
        cy.BaseRequest('GET', baseUrl).then((response)=>{
            expect(response.status).to.equal(200)
            response.body.forEach(element => {
                cy.ContractValidation(element)
            });
        } )
    })
    it('Search for a specific books with success', ()=>{
        const url = baseUrl + "/10"
        cy.BaseRequest('GET', url).then((response)=>{
            expect(response.status).to.equal(200)
            expect(response.body.id).to.equal(10)
            cy.ContractValidation(response.body)
        })
    })
    it('Search for a specific books with invalid Id', ()=>{
        const url = baseUrl + "/10AAA"
        cy.BaseRequest('GET', url).then((response)=>{
            expect(response.status).to.equal(400)
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

使用其他方法的命令:
DELETE

describe('Testing DELETE Method', ()=>{
    const baseUrl = "https://fakerestapi.azurewebsites.net/api/v1/Books"
    it('Delete a book with success', ()=>{
        cy.BaseRequest('DELETE', baseUrl + "/100").then((response)=>{
            expect(response.status).to.equal(200)
        })
    })

    it('Update a book passing invalid ID', ()=>{
        cy.BaseRequest('DELETE', baseUrl + "/invalid").then((response)=>{
            expect(response.status).to.equal(400)
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

对于 PUT 和 POST 方法,我们可以创建另一个命令来验证主体响应的内容并与发送的内容进行比较:

Cypress.Commands.add('ResponseValidation', (requestBody, responseBody)=>{
    expect(responseBody.id).to.equal(requestBody.id)
    expect(responseBody.title).to.equal(requestBody.title)
    expect(responseBody.description).to.equal(requestBody.description)
    expect(responseBody.pageCount).to.equal(requestBody.pageCount)
    expect(responseBody.excerpt).to.equal(requestBody.excerpt)
    expect(responseBody.publishDate).to.equal(requestBody.publishDate)
})
Enter fullscreen mode Exit fullscreen mode

我们的测试将是:

describe('Testing PUT Method', ()=>{
    const baseUrl = "https://fakerestapi.azurewebsites.net/api/v1/Books"
    const body = {
        id: 100,
        title: "string1 updated",
        description: "string1 updated",
        pageCount: 100,
        excerpt: "string",
        publishDate: "2023-10-14T18:44:34.674Z"
    }
    it('Update an book with success', ()=>{
        cy.BaseRequest('PUT', baseUrl + "/100", body).then((response)=>{
            expect(response.status).to.equal(200)
            cy.ContractValidation(response.body)
            cy.ResponseValidation(body, response.body)
            })
    })
    it('Update a book passing invalid ID', ()=>{
        cy.BaseRequest('PUT', baseUrl + "/invalid", body).then((response)=>{
            expect(response.status).to.equal(400)
        })
    })
    it('Update a book with empty body request', ()=>{
        const body = {}
        cy.BaseRequest('PUT', baseUrl + "/100", body).then((response)=>{
            expect(response.status).to.equal(200)
            cy.ContractValidation(response.body)
        })
    })
})
Enter fullscreen mode Exit fullscreen mode
describe('Testing POST Method', ()=>{
    const baseUrl = "https://fakerestapi.azurewebsites.net/api/v1/Books"
    it('Create a book with success', ()=>{
        const body = {
            id: 100,
            title: "string1",
            description: "string1",
            pageCount: 100,
            excerpt: "string",
            publishDate: "2023-10-14T18:44:34.674Z"
        }
        cy.BaseRequest('POST', baseUrl, body).then((response)=>{
            expect(response.status).to.equal(200)
            cy.ContractValidation(response.body)
            cy.ResponseValidation(body, response.body)
        })
    })

    it('Create a book without body request', ()=>{
        cy.BaseRequest('POST', baseUrl).then((response)=>{
            expect(response.status).to.equal(415)
        })
    })

    it('Create a book with empty body request', ()=>{
        const body = {}     
        cy.BaseRequest('POST', baseUrl, body).then((response)=>{
            expect(response.status).to.equal(200)           
            cy.ContractValidaton(response.body)     
        })
    })
})
Enter fullscreen mode Exit fullscreen mode

结论

在本教程中,我们将学习什么是 Cypress 框架以及如何使用它来自动化 API 测试。需要注意的是,这只是使用命令自动化 API 测试的一种选择,但正如我们所见,使用这种方法可以帮助我们使测试更简单、更易于维护、更易于阅读。

您可以在此处访问本教程中使用的项目 。 

希望这些内容对您有所帮助。 

如果您有任何疑问,请随时与我联系!

鏂囩珷鏉ユ簮锛�https://dev.to/m4rri4nne/api-testing-with-cypress-part-i-2k3i
PREV
使用 Performance.now() 对字符串字面量 ("") 和模板字面量 (``) 进行基准测试
NEXT
创建更清洁的 MacOS 工作区