😏作者简介:博主是一位测试管理者,同时也是一名对外企业兼职讲师。 📡主页地址:【Austin_zhai】 🙆目的与景愿:旨在于能帮助更多的测试行业人员提升软硬技能,分享行业相关最新信息。 💎声明:博主日常工作较为繁忙,文章会不定期更新,各类行业或职场问题欢迎大家私信,有空必回。
在上一篇 《Cypress安装与使用教程(1)—— 软测大玩家》,我们熟悉了Cypress的一些基本安装与使用的方法。对于一些E2E的测试场景,该软件的业务落地表现还是比较让人满意的。接下来我们将在之前的基础上来认识一些日常高频的Cypress使用技巧。
在Cypress中,钩子函数(Hooks)的作用是可以让我们在不同的测试生命周期阶段执行特定的代码,以便进行全局的设置、准备工作或清理工作。如果要确保测试用例在不同阶段的执行过程中能保证其目的正确性、可维护性和可靠性的话,钩子函数则是我们的不二之选。
使用before()和beforeEach()钩子函数可以在测试运行之前执行一些全局的设置和准备工作。包括创建测试数据、启动应用程序、设置测试环境等。
before()
before(() => {
// 在所有测试运行之前执行的代码
});
beforeEach()
beforeEach(() => {
// 在每个测试运行之前执行的代码
});
使用这两个函数可以在测试运行之后执行一些全局的清理工作。比如测试完成后进行清理测试数据、关闭应用程序、还原测试环境。
after()
after(() => {
// 在所有测试运行之后执行的代码
});
afterEach()
afterEach(() => {
// 在每个测试运行之后执行的代码
});
另外,我们在使用钩子函数时可以允许你在测试生命周期中共享状态。只需要在before()中设置一些全局变量,然后在各个测试用例中使用。这样我们就可以达到即使在不同的测试之间传递信息,确保测试的一致性;
接下来我们来看一段代码,其中包含了相关的钩子函数的使用方法。
before(() => {
// 登录操作,确保测试前用户已登录
cy.visit('https://www.rubies-fund.com/login');
cy.login('your_username', 'your_password');
});
// 在每个测试运行之前执行的代码,例如导航到基金购买页面
beforeEach(() => {
// 导航到基金购买页面
cy.visit('https://example.com/invest/funds');
});
// 在每个测试运行之后执行的代码,例如清理购买时的状态
afterEach(() => {
// 清理购买操作的状态,确保下一次测试开始前的干净状态
cy.clearFundPurchaseState();
});
// 在所有测试运行之后执行的代码,例如退出登录
after(() => {
// 退出登录,确保测试结束后用户已退出
cy.logout();
});
// 实际的测试用例
describe('Fund Purchase Demo', () => {
it('should allow the user to purchase a fund', () => {
// 这里编写测试购买基金的代码
cy.purchaseFund('FundABC', 1000); // 示例购买基金 'FundABC',金额为 1000
cy.verifyPurchaseSuccess(); // 验证购买是否成功
});
it('should display correct fund details after purchase', () => {
// 这里编写测试购买后基金详情是否正确显示的代码
cy.purchaseFund('FundXYZ', 500); // 示例购买基金 'FundXYZ',金额为 500
cy.navigateToFundDetails('FundXYZ'); // 导航到购买后的基金详情页面
cy.verifyFundDetails('FundXYZ', 500); // 验证基金详情是否正确显示
});
});
从以上的代码我们可以看到比较明显的业务流程,其中的每个测试用例都包含了一些关于基金购买的操作,比如购买基金和验证购买结果。通过使用钩子函数,可以确保测试在执行前后的状态的一致。
既然涉及到web的E2E测试,那元素定位依然是一个无法回避的问题,它是告诉测试脚本在页面上找到并与之交互的特定元素的方法。这就像在页面中找到你想要点击、输入或验证的那个按钮或文本框一样。定位元素的方式虽然没有selenium与appium那样多样化,但也已经足够我们使用了。
这个关键字相信大家都不会陌生,我们可以通过contains来进行页面元素的模糊匹配,使用方法如下。
比如在页面上我们需要定位一个名叫"支付"的按钮,可以直接使用contains+关键字的方式来进行定位。
cy.contains('支付');
利用元素的一些特定属性(class,id,css_selector)等来进行定位。
例如按钮的类名为fg-button,id名为trade-ned,我们就可以使用以下的方式来进行准确的定位。
cy.get('.fg-button');
cy.get('#trade-ned');
除此之外,我们还可以进行属性的自定义组合或索引,来更加快速高效的找到元素。
cy.get('[data-testid="dep-button"]');
找列表中的第三个元素
cy.get('ul li:eq(2)');
根据父子关系
cy.get('.parent-class').find('.child-class');
接下来我们用一段html代码来具体演示一下各类元素定位的方式
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Element Locators Demo</title>
</head>
<body>
<h1>Welcome to the Element Locators Demo</h1>
<div id="container">
<button class="my-button" data-testid="submitBtn">Submit</button>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<form>
<label for="username">Username:</label>
<input type="text" id="username" name="username">
<label for="password">Password:</label>
<input type="password" id="password" name="password">
<button class="my-button" id="loginBtn">Login</button>
</form>
<footer>
<p>Contact us at: <a href="mailto:info@example.com">info@example.com</a></p>
</footer>
</body>
</html>
// 通过文本的方式进行定位
it('should locate button by text content', () => {
cy.contains('Submit').click();
});
// 通过id名的方式来进行定位
it('should locate login button by ID', () => {
cy.get('#loginBtn').click();
});
// 通过类名的方式进行定位
it('should locate button by class name', () => {
cy.get('.my-button').click();
});
// 通过自定义属性的方式进行定位
it('should locate button by data-testid attribute', () => {
cy.get('[data-testid="submitBtn"]').click();
});
// 通过索引定位的方式进行定位
it('should locate list item by index', () => {
cy.get('ul li:eq(1)').should('contain', 'Item 2');
});
// 通过父子关系的方式来进行定位
it('should locate input field by parent-child relationship', () => {
cy.get('form').find('#username').type('myUsername');
});
在E2E测试中,我们有时需要在页面中对于元素进行循环操作或查找,那么元素遍历就像是你在超市里逛逛,检查每个过道的商品一样,以达到在页面上循环查找和交互多个元素的效果。
使用 .each() 来遍历列表中的元素
cy.get('ul li').each(($item, index) => {
cy.log(`Item ${index + 1}: ${$item.text()}`);
});
同样是使用.each() 来遍历一组元素并进行特定的操作
// 点击每一个元素的商品
cy.get('.product').each(($product) => {
cy.wrap($product).click();
});
我们使用.filter() 来进行元素的过滤并进行遍历
cy.get('.product').filter('.vegetables').each(($vegetable) => {
cy.log(`Found a vegetable: ${$vegetable.text()}`);
});
使用.find() 在指定的父元素中进行遍历与查找
cy.get('.room').find('.person').each(($person) => {
cy.log(`Found a person: ${$person.text()}`);
});
使用.each() 进行逐级遍历,当然这里是需要用到嵌套才能达到这样的效果
cy.get('.building').each(($floor) => {
cy.wrap($floor).find('.room').each(($room) => {
cy.log(`Found a room: ${$room.text()}`);
});
});
以上就是一些Cypress的高频使用技巧,另外我们在使用的时候也需要注意一些特定的情况,比如使用钩子函数时可能会出现异步操作,特别是一些比较耗时的网络访问业务操作,可以在我们的脚本中有针对性的等待前置操作完成再执行所需要的操作等步骤。在我们的元素遍历中,如果能提前做好一些异常的处理的话,可以让我们的元素操作更为的健壮,其实无论是何种自动化脚本来说,这些都是必须考虑进去的重要因素。