上次博客介绍了如何调用接口或者数据库准备测试数据,此次课程将学习如何管理测试数据,还是三个问题,需要管理哪些数据?为什么要管理这些数据,如何管理这些数据?
首先,我们来思考第一个问题,需要管理哪些测试数据,实际一切你想动态传递给测试场景的数据都可以通过文件管理起来,这样在测试场景中需要使用这些测试数据时从对应的文件中读取,当测试数据发生改变时只需修改文件中的内容即可。那么哪些数据需要动态传递给测试场景呢?归纳起来有两类。
-
第一类:是多个测试场景中都会使用的测试数据,这类测试数据需要统一管理在文件中,当测试数据改变时只需修改文件中的内容,所有测试场景的测试数据都同步改变了。例如运行所有测试场景前,需要先初始化需要的用户以及给不同的用户赋权,那么用户名称,登陆密码,需要赋权的角色名称等信息需要放到文件中管理,这样才能在运行所有用例前完成初始化工作。
-
第二类:不同测试环境中相同的测试场景下传入的测试数据不相同,这类数据需要按测试环境放到不同的文件中,测试场景读取时,需要先获取测试环境值,再读取对应环境的这个数据管理文件中的值,这样测试环境切换后,不会因为测试数据的变化导致用例运行失败。
回答了第一个问题后,我们来思考第二个问题,为什么要管理这些数据?原因总结起来实际也是两类,第一是缩小维护成本、第二是提高自动化脚本的稳定性。
例如,相同的测试数据多个测试场景都会使用,以登陆系统为例,所有的用例运行时都需要先登陆系统,那登陆的用户信息就需要统一管理,这样如果某一天用户修改了密码,只需修改数据文件,不用修改所有测试场景的登陆部分脚本。这就是一个典型的如何通过管理测试数据缩小维护成本的例子。
再比如,系统中存在一个根据订单编号检索信息的功能,订单编号以及订单编号关联的信息来自第三方系统,自己负责的系统DEV环境连接的是三方系统的DEV环境,ST环境连接三方系统的ST环境,且连接的三方系统的DEV和ST环境中有效的订单号不同。针对这种情况就需要按环境管理订单编号信息,否则切换环境后,用例就失败了。此时如果你没按环境管理订单编号信息,某个环境运行正常,但切换环境后,用例就失败,这就是一个典型的如何通过管理测试数据提升脚本稳定性的例子。
回答了第二个问题后,我们再来思考第三个问题,如何管理测试数据?一句话概括之用文件存放测试数据,需要时从文件中读取。
那用什么文件存放测试数据呢?通常情况下自动化脚本中都用csv文件存放测试数据,但如果测试数据量比较小时也可以用json文件存放测试数据,用json文件存放测试数据时,很容易读取到文件中内容。此章节主要演示如何用json文件存放测试数据。在后面讲解puppeteer框架时会介绍如何用csv文件和json文件存放测试数据。
Cypress提供了两种方式读取json文件中的内容,第一种是调用cypress中的readFile()方法,第二种是调用fixture()方法。两种方法使用很相似,唯一的区别是使用readFile()方法时需要传入测试数据文件相对于代码根目录的全路经信息,fixture()方法只需传入相对于fixture目录的相对路径即可。在实际项目中可以选任意其中一种方式。
下面来看一看如何使用这两个方法读取json文件中的测试数据。当用命令初始化项目后,会在cypress目录下自动生成fixture目录。当调用fixture()方法时,需要把数据文件放到fixture目录下。这里,在fixtures目录下创建testData目录,并把所有存放测试数据的文件放到该目录下。如下图所示,user.json里面存放了两个登陆用户信息,另外还创建st目录,dev目录,如果随着环境变化而变化的测试数据可以按环境放到st和dev目录中
接下来看一下如何读取testData目录下user.json文件的内容,可以看到readFile和fixture两种读取方式,只是传入的文件相对路径有差别,其他使用方式都一样。
it("read test data in json file with readFile function",() => {
cy.readFile('cypress/fixtures/testData/user.json')
//使用readFile读取json文件内容,传入的文件路径是相对于代码根目录的相对路径
.then((data) => {
console.log(data);
console.log('the username is: '+data.regular.name);
})
}
);
it ("read test data in json file with fixture function", ()=> {
cy.fixture('testData/user.json')
//使用fixture读取json文件中,传入的文件路径是相对于fixtures目录的相对路径
.then((data) => {
console.log(data);
console.log('the username is: '+data.regular.name);
})
});
同样,Test Runner中选择“readTestData_spec.js”即可运行上面的脚本。因为上述脚本是用console.log()打印读取到的文件内容,故在调试时需要打开浏览器的DevTools。结果如下图所示,可以看到浏览器的console中打印出了用户信息,说明正确获取到了user.json文件中的内容。
除上面采用cy.readFile('文件路径').then((data)=> {......}) 和cy.fixture('文件路径').then((data)=> {......})方式获取测试数据文件中内容外,还可以采用别名方式。以下是采用别名方式读取测试文件中的内容。可以看到采用别名方式只是在cy.readFile(....)或者cy.fixture(.....)后面添加了as('数据别名'),在需要测试数据的地方采用cy.get('@数据别名').then((data)=>{.....})获取测试数据。同样,Test Runner中选择“readTestDataByAlias_spec.js”即可运行下面的脚本。
it("read test data in json file with readFile function", () => {
cy.readFile('cypress/fixtures/testData/user.json').as('users');
//as('别名名称')
cy.get('@users').then((data) => {
//获取文件中内容时采用cy.get('@别名名称').then((data)=>{.....})
console.log(data);
console.log('the username is: ' + data.regular.name);
})
}
);
it("read test data in json file with fixture function", () => {
cy.fixture('testData/user.json').as('usersTwo');
//as('别名名称')
cy.get('@usersTwo').then((data) => {
//获取文件中内容时采用cy.get('@别名名称').then((data)=>{.....})
console.log(data);
console.log('the username is: ' + data.regular.name);
})
});
相较于上面两种读取数据的方式,第二种别名方式的优势是可以把读取文件和使用数据分开。即先在专门的js文件中读取各种需要的测试文件,然后在需要测试数据的测试场景中,使用cy.get('测试数据别名')获取测试数据。例如,创建名称为"manageTestData.js"的文件,专门用户读取各种测试数据文件。
function getDifEnvTestData(fileName,testDataAlias) {
cy.fixture('testData/'+Cypress.env('appEnv')+fileName).as(testDataAlias)
//这里利用环境变量“appEnv”读取不同环境下的测试数据,下次课程将讲解如何管理配置信息
}
function getCommonTestData(fileName,testDataAlias) {
cy.fixture('testData/'+fileName).as(testDataAlias)
}
function getUserTestData(testDataAlias) {
getCommonTestData('user.json',testDataAlias)
}
function getArticleTestData(testDataAlias) {
getDifEnvTestData('articleDetails.json',testDataAlias)
}
module.exports= {
getUserTestData: getUserTestData,
getArticleTestData: getArticleTestData
};
登陆测试场景中,输入的用户名、密码从测试文件中获取,第一个测试场景读取user.json文件中regular的用户信息
const manageTestDta = require('./manageTestData');
describe("add comment for a article",()=> {
it('should add comment for a article with correct login user successfully', () => {
cy.visit("https://angular.realworld.io");
cy.get('app-layout-header ul li a[href="/login"]').click();
manageTestDta.getUserTestData('users');
//获取user.json中内容,测试数据别名是“users”
cy.get('@users').then((data) => {
//获取读取的测试数据信息
cy.get('[formcontrolname=email]').type(data.regular.name);
//登陆的时候输入的用户名称是从user.json文件中获取的用户名称
cy.get('[formcontrolname=password]').type(data.regular.password);
//登陆的时候输入的用户名称是从user.json文件中获取的密码信息
});
cy.get('app-auth-page button[type="submit"]').click();
})
});
Test Runner中选择“loginWithUserFromFile_spec.js”文件即可运行上述测试场景,如果运行成功,说明正确读取到了user.json文件中的用户信息。
另外,可以看到user.json文件中除了regular用户,还有manager用户,如果读取manager的用户名、密码,登陆应该会失败,因为密码是错误的密码。读取manager的用户信息测试脚本如下所示。同样,选择“loginWithWrongUser_spec.js”文件即可运行下面的脚本。
const manageTestDta = require('./manageTestData');
describe("add comment for a article",()=> {
it('should add comment for a article with correct wrong user failed', () => {
cy.visit("https://angular.realworld.io");
cy.get('app-layout-header ul li a[href="/login"]').click();
manageTestDta.getUserTestData('users');
cy.get('@users').then((data) => {
cy.get('[formcontrolname=email]').type(data.manager.name);
//读取manager的用户名
cy.get('[formcontrolname=password]').type(data.manager.password);
//读取manager的密码信息
});
cy.get('app-auth-page button[type="submit"]').click();
})
});
上面演示了测试数据管理的demo,这里再总结下测试数据管理要点。
-
1.如果多个测试场景需要共用的测试数据信息,那么可以用json文件或者csv文件存储,并封装读取测试数据方法,这样当测试场景中需要准备测试数据时,可以调用封装的方法获取测试数据信息。
-
2.如果是多个测试案例共用的测试数据称为共享测试数据,这样的测试数据在测试案例运行过程中不应该被修改。例如多个测试场景需要用管理员用户A登陆,那么任何测试案例不能修改登陆用户A的权限信息和登陆名信息,否则其他案例运行时可能会失败。
-
3.因为共享测试数据在任何测试案例运行过程中不被修改,那么可以把这类测试数据的准备工作封装到js文件中,并在所有案例运行前执行,CICD平台上可以配置多个job完成自动化案例运行。
-
4.如果某个测试案例运行过程中会修改需要的测试数据,这类测试数据称为案例独占的测试数据。此类测试数据应该在测试案例运行前准备,案例运行完成后清理,即放在测试脚本的before()和after()中。
-
5.测试案例独占的测试数据不要共享,否则某个测试案例修改测试数据后,可能导致其他测试案例运行失败。
-
6.为了提升UI层脚本的稳定性,建议通过查询数据库或者调用接口准备和清理测试数据。
-
7.测试数据准备和清理脚本需要足够健壮。例如,如果某人手动修改了测试数据的部分内容,自动化脚本运行时能自动检查测试数据是否符合要求,并进行自我修复。
-
8.做好测试数据的管理是提升自动化脚本稳定性的关键措施之一,需要根据项目实际情况持续优化。