使用 Cypress 进行前端自动化测试的最佳实践
作者:互联网
最佳自动化实践是指一组用于创建有效和高效的自动化测试的准则或建议。这些实践涵盖了测试过程的各个方面,包括规划、设计、实施、执行和维护自动化测试。
Cypress 是一种流行的测试工具,由于其用户友好的测试框架和简化测试过程的内置功能,近年来越来越受欢迎。
但是,如果您没有正确使用它并且不遵循 Cypress 的最佳实践,您的测试性能将受到严重影响,您将犯不必要的错误,并且您的测试代码将不可靠且不稳定。
最新的DZone参考卡
云原生应用安全
作为测试自动化工程师,您应该遵守 Cypress 项目中的某些最佳实践,以便代码更有效、可重用、可读且可维护。
赛普拉斯最佳实践
在这篇博客中,我们将讨论 QA 工程师在使用 Cypress 进行前端自动化测试时应该知道的最佳实践。
以下是使用赛普拉斯的一些最佳实践:
1. 避免不必要的等待
在 Cypress 测试中使用静态等待命令(如 cy. wait(timeout))通常被认为是一种不好的做法,因为它们会引入不必要的延迟,并且会使测试的可靠性降低。
cy.visit('https://talent500.co/')
等(5000)
在此代码片段中,cy.visit() 命令用于导航到网站和城市。wait(5000) 命令用于暂停测试执行五秒钟,然后再继续执行以下命令。
即使站点在 2 秒系统中加载,也要等到 <> 秒。处理此问题的更好方法是使用 cy.intercept() 使用动态等待。
JavaScript1
cy.intercept(‘/api/data’).as(‘getData’)
2
3
cy.visit(‘https://example.com/’)
4
5
cy.wait(‘@getData’)
6
7
.its(‘response.statusCode’)
8
9
.should(‘eq’, 200)
在此示例中,我们将拦截对 /api/data 的网络请求,并为其提供 getData 的别名。然后,我们导航到示例页面并等待 getData 请求使用 cy 完成。等待(“@getData”)。最后,我们检查响应的状态代码是否为 200。
2. 明智地使用钩子
在 Cypress 中,我们有四个钩子,即 before()、beforeEach()、after() 和 afterEach()。
编写重复的代码不是一个聪明的主意。假设我们有五个测试用例,在每个测试用例中,我们都有一组在每个测试用例中通用的行。因此,处理重复代码的更好方法是使用钩子;在这种情况下,您可以使用 beforeEach()。
谨慎使用 before() 和 after() 钩子:由于这些钩子对整个套件执行一次,因此它们可能很慢,并且更难隔离测试失败。相反,请尝试使用 beforeEach() 和 afterEach() 挂钩来设置和清理测试场景。
例:
在下面的例子中,你可以看到我们在 before() 和 after() 之前使用了钩子。在这种情况下,这些钩子最匹配,但我们不需要总是使用这些钩子,完全取决于需求。
JavaScript1
describe(“Login into talent500.”, () => {
2
3
before(() => {
4
5
cy.visit(“https://talent500.co/auth/signin”);
6
7
cy.viewport(1280,800)
8
9
});
10
11
it(“Login into talent500”, () => {
12
13
cy.get(‘[name=”email”]’).type(“applitoolsautomation@yopmail.com”,{force: true} );
14
15
cy.get(‘[name=”password”]’).type(“Test@123”,{force: true});
16
17
cy.get(‘[data-id=”submit-login-btn”]’).click({force: true});
18
19
cy.contains(“John”);
20
21
});
22
23
it(“Verify user is logged in by verifying text ‘Contact us’ “, () => {
24
25
cy.contains(“Contact us”);
26
27
});
28
29
after(() => {
30
31
cy.get(‘[data-id=”nav-dropdown-logout”]’).click({force: true});
32
33
});
34
35
});
下面是上述脚本的测试用例执行的屏幕截图。
使用 beforeEach() 和 afterEach() 钩子来设置和清理测试场景:通过使用这些钩子重置测试之间的状态,可以使测试更加独立,并降低副作用和片状测试的风险。
例
在此示例中,beforeEach() 钩子用于访问登录页面并填写电子邮件和密码字段,然后单击登录按钮。it() 测试检查“联系我们”链接是否在仪表板上可见。afterEach() 钩子用于单击注销按钮。
JavaScript1
describe(“Login Test Suite”, () => {
2
3
beforeEach(() => {
4
5
cy.visit(“https://talent500.co/auth/signin”);
6
7
cy.get(‘[name=”email”]’).type(“applitoolsautomation@yopmail.com”, {
8
9
force: true,
10
11
});
12
13
cy.get(‘[name=”password”]’).type(“Test@123”, { force: true });
14
15
cy.get(‘[data-id=”submit-login-btn”]’).click({ force: true });
16
17
});
18
19
it(“should display the dashboard”, () => {
20
21
cy.contains(“Contact us”).should(“be.visible”);
22
23
});
24
25
afterEach(() => {
26
27
cy.get(‘[data-id=”nav-dropdown-logout”]’).click({ force: true });
28
29
});
30
31
});
3. 在柏树配置文件中设置 baseUrl
在每个规范文件的 before() 块中使用 cy.visit() 对 baseUrl 进行硬编码并不是最好的方法,因为它会导致冗余代码并使测试更难维护。
更好的方法是在 cypress.config.js 配置文件中设置 baseUrl,并在测试文件中使用带有相对 URL 的 cy.visit() 命令。
例如,在 cypress.json 文件中,您可以将 baseUrl 设置为登录页面 URL。
JavaScript1
{
2
3
“baseUrl”: “https://www.example.com/login”
4
5
}
然后,在测试文件中,可以使用相对 URL 导航到应用程序的其他页面。例如:
JavaScript1
describe(‘My Test Suite’, () => {
2
3
beforeEach(() => {
4
5
cy.visit(‘/’)
6
7
})
8
9
it(‘should perform some test’, () => {
10
11
// Perform some test
12
13
})
14
15
})
4. 使用 data-cy“ 属性标识定位器
使用 data-cy 属性来标识定位器是 Cypress 中的最佳实践,因为它提供了一种可靠且一致的方式来定位应用程序中的元素。data-cy 属性是一个自定义属性,您可以将其添加到应用程序中的任何元素中,以便更轻松地在 Cypress 测试中定位该元素。
下面是如何使用 data-cy 属性来定位应用程序中的登录表单的示例:
假设我们有以下用于登录表单的 HTML 代码。
.HTML1
<form>
2
3
<label for=“email”>Email:</label>
4
5
<input type=“email” id=“email” name=“email”>
6
7
<label for=“password”>Password:</label>
8
9
<input type=“password” id=“password” name=“password”>
10
11
<button type=“submit”>Login</button>
12
13
</form>
要使用“data-cy-*”属性来识别定位器,您可以将“data-cy”属性添加到我们要在测试中选择的每个元素。例如:
.HTML1
<form>
2
3
<label for=“email”>Email:</label>
4
5
<input type=“email” id=“email” name=“email” data-cy=“email-input”>
6
7
<label for=“password”>Password:</label>
8
9
<input type=“password” id=“password” name=“password” data-cy=“password-input”>
10
11
<button type=“submit” data-cy=“login-button”>Login</button>
12
13
</form>
14
现在,您可以使用这些 data-cy 属性在我们的 Cypress 测试中选择元素,如下所示:
描述('登录表单', () => {
JavaScript1
it(‘should allow a user to log in’, () => {
2
3
cy.visit(‘/login’)
4
5
cy.get(‘[data-cy=”email-input”]’).type(‘testuser@example.com’)
6
7
cy.get(‘[data-cy=”password-input”]’).type(‘password123’)
8
9
cy.get(‘[data-cy=”login-button”]’).click()
10
11
})
12
13
})
5. 隔离它() 块
最佳做法是使用独立的 it() 块,该块不依赖于任何其他 it() 块的结果即可成功运行。每个 it() 块都应该有自己的设置和拆卸步骤,并且不应该依赖于任何先前测试中的应用程序状态。
以下是在 Cypress 中使用独立 it() 块的一些好处:
- 测试独立运行:通过使用独立的 it() 块,您可以确保每个测试都独立运行,而不依赖于任何先前测试的应用程序状态。这使我们的测试更可靠,更易于维护。
- 测试更容易理解:独立的 it() 块更容易理解,因为每个块代表一个特定的测试用例。这样可以更轻松地排除故障和修复问题。
- 测试运行得更快:由于每个 it() 块都是隔离运行的,因此测试运行得更快,因为不需要为每个测试设置应用程序的状态。
下面是如何在 Cypress 中使用独立 it() 块的示例:
JavaScript1
describe(“Login into talent500 With invalid crediential”, () => {
2
3
beforeEach(() => {
4
5
cy.visit(‘/signin’)
6
7
cy.viewport(1280, 800);
8
9
});
10
11
it(“Login into talent500 when Email is incorrect”, () => {
12
13
cy.get(‘[name=”email”]’).type(“applitoolsautomation11@yopmail.com”, {
14
15
force: true,
16
17
});
18
19
cy.get(‘[name=”password”]’).type(“Test@1234”, { force: true });
20
21
cy.get(‘[data-id=”submit-login-btn”]’).click({ force: true });
22
23
cy.contains(“Unable to login with the provided credentials”);
24
25
});
26
27
it(“Login into talent500 when Password is incorrect”, () => {
28
29
cy.get(‘[name=”email”]’).type(“applitoolsautomation@yopmail.com”, {
30
31
force: true,
32
33
});
34
35
cy.get(‘[name=”password”]’).type(“Test@123444”, { force: true });
36
37
cy.get(‘[data-id=”submit-login-btn”]’).click({ force: true });
38
39
cy.contains(“Unable to login with the provided credentials”);
40
41
});
42
43
});
输出:
在下面的屏幕截图中,您可以看到两个测试用例独立运行并通过。
6. 每个测试多个断言
在一个测试中编写单个断言可能会减慢测试速度并导致性能问题。最佳做法是使用单个命令添加多个断言,使测试更快、更好地进行组织和清晰度。
JavaScript1
it(“Login into talent500 when Email is incorrect”, () => {
2
3
cy.get(‘[name=”email”]’).type(“applitoolsautomation@yopmail.com”,{ force: true }).should(“have.value”, “applitoolsautomation@yopmail.com”)
4
5
.and(“include.value”, “.”)
6
7
.and(“include.value”, “@”)
8
9
.and(“not.have.value”, “test@qa.com”)
10
11
cy.get(‘[name=”password”]’).type(“Test@1234”, { force: true });
12
13
cy.get(‘[data-id=”submit-login-btn”]’).click({ force: true });
14
15
cy.contains(“Unable to login with the provided credentials”);
16
17
});
18
输出:
在下面的屏幕截图中,您可以看到我们已经在一行中验证了所有断言,而不是为不同的断言编写不同的代码行。
7. 保持测试数据独立
将测试数据与测试代码分开是 Cypress 自动化的最佳实践。与测试代码分开的数据有助于使测试更易于维护且更易于更新。
下面是如何在赛普拉斯中保持测试数据分离的示例:
- 创建一个单独的文件来存储测试数据。例如,您可以在项目的根目录中创建一个名为“test-data.json”的 JSON 文件。
- 在此文件中,将测试数据定义为键值对。例如:
1
{
2
3
“username”: “testuser”,
4
5
“password”: “testpassword”
6
7
}
在测试代码中,使用 Cypress 夹具从 JSON 文件导入测试数据。
从“../fixtures/test-data.json'
通过引用 JSON 文件中定义的键,在测试中使用测试数据。例如:
JavaScript1
describe(‘Login’, () => {
2
3
it(‘should login successfully’, () => {
4
5
cy.visit(‘/login’)
6
7
cy.get(‘#username’).type(testData.username)
8
9
cy.get(‘#password’).type(testData.password)
10
11
cy.get(‘#login-btn’).click()
12
13
cy.url().should(‘include’, ‘/dashboard’)
14
15
})
16
17
})
通过将测试数据存储在单独的文件中,您可以轻松地更新测试数据,而无需修改测试代码本身。
8. 使用别名
使用别名将命令链接在一起,而不是在每个命令中重复相同的选择器。这使得测试代码更具可读性和可维护性。
例如,您可以为登录按钮设置别名,然后在后续测试中使用它,而无需再次找到它。
在此示例中,我们使用“as”命令为电子邮件输入、密码输入和提交按钮分配别名。然后,我们在测试本身中使用这些别名。这使得测试更具可读性且更易于维护,尤其是在有多个测试使用相同的选择器时。
JavaScript1
describe(“Login into talent500.”, () => {
2
3
beforeEach(() => {
4
5
cy.visit(‘/signin’)
6
7
cy.get(‘[name=”email”]’).as(’emailInput’)
8
9
cy.get(‘[name=”password”]’).as(‘passwordInput’)
10
11
cy.get(‘[data-id=”submit-login-btn”]’).as(‘loginButton’)
12
13
});
14
15
it(“Login into talent500 when Email is incorrect”, () => {
16
17
cy.get(‘@emailInput’).type(“applitoolsautomation@yopmail.com”,{ force: true })
18
19
.should(“have.value”, “applitoolsautomation@yopmail.com”)
20
21
.and(“include. value”, “.”)
22
23
.and(“include.value”, “@”)
24
25
.and(“not.have.value”, “test@qa.com”)
26
27
cy.get(‘@passwordInput’).type(“Test@1234”, { force: true });
28
29
cy.get(‘@loginButton’).click({ force: true });
30
31
cy.contains(“Unable to login with the provided credentials”);
32
33
});
34
35
it(“Login into talent500 when Password is incorrect”, () => {
36
37
cy.get(‘@emailInput’).type(“applitoolsautomation@yopmail.com”, {
38
39
force: true,
40
41
});
42
43
cy.get(‘@passwordInput’).type(“Test@123444”, { force: true });
44
45
cy.get(‘@loginButton’).click({ force: true });
46
47
cy.contains(“Unable to login with the provided credentials”);
48
49
});
50
51
it(“Login into talent500 with valid credentials “, () => {
52
53
cy.get(‘@emailInput’).type(“applitoolsautomation@yopmail.com”, {
54
55
force: true,
56
57
});
58
59
cy.get(‘@passwordInput’).type(“Test@123”, { force: true });
60
61
cy.get(‘@loginButton’).click({ force: true });
62
63
cy.get(‘[data-id=”nav-dropdown-logout”]’).click({ force: true });
64
65
});
66
67
});
输出
在下面的屏幕截图中,您可以在日志中看到三个别名,并且测试用例已成功执行。