前面博客介绍了如何使用puppeteer框架查找和操作页面元素,熟悉了常用的操作后即可编写完整的UI层自动化案例。但实际项目中要搭建一套完善的UI层自动化脚本,掌握测试框架对外提供的方法还远远不够,正如puppeteer章节最开始讲的一样,我们还得考虑数据准备、配置信息管理内容。在数据准备部分主要有如下三种方式
- 调用接口准备测试数据
- 调用数据库准备测试数据
- 页面操作准备测试数据
针对上面三种方式,首选接口调用方式准备测试数据,因为每个自动化测试场景的重点是验证该自动化用例需要验收的点,数据准备部分应尽量保证稳定和简洁,这样整个自动化脚本稳定性会更好。
其次是调用数据库方式准备数据,理论上大部分测试数据肯定都能调用接口准备,如果确实某些测试数据无法调用接口准备,且通过写入数据库方式比较容易,那么可以选择操作数据库准备测试数据。此次课程主要介绍如何调用接口准备测试数据,下次课程会介绍连接数据库准备测试数据。
如果调用接口和连接数据库都不易准备的测试数据,那么可以通过点击页面操作完成数据准备。 数据准备好后再执行真正的测试场景,测试场景执行完成后再通过页面操作删除测试数据。此策略最大的劣势是增加了脚本不稳定因素。UI层自动化脚本比较痛的一个点就是脚本自身的稳定性,如果所有数据准备都通过页面操作完成,势必降低整个脚本稳定性,稳定性低就意味着自动化维护成本的提高,故建议如果能通过调用接口或连接数据库准备的测试数据,不要使用页面操作来准备。接下来就开始第一个task吧。
调用接口准备测试数据
如何用js语言实现接口调用呢?请看下面的案例,同样执行“npm run call-api“即可运行下面的案例。
需要注意一点是这里的案例调用的是本地启动的mock的接口,如果要保证案例运行成功,需要下载mock-server代码( git clone https://github.com/tlqiao/wiremock-demo ),该mock-server的代码使用maven作为构建工具,所以需要安装maven和jdk并执行“mvn clean install”安装对应依赖,接着通过运行代码中“Application” class启动服务,服务启动后,请先在postman工具中调用该接口,保证mock的接口能正常访问后才执行下面的自动化脚本。
const rp= require('request-promise');
// 引入request-promise,接口调用返回的是一个promise对象
const rq=require('request');
//引入request,也可完成接口调用,就UI层自动化测试调用接口准备测试数据场景而言,两个包都可以,大家可以任选其中一个即可
//以下文件中包含3个调用接口的方法,案例中包含调用Post的接口和Get的接口
function callPostApi() {
const options = {
method: "POST",
uri: "http://localhost:9090/api/addBookWithAnyBody/abc",
headers: {
"Content-Type": "application/json"
},
body: {"name": "noBody"},
json: true
};
//上面代码实现将调用接口相关的method,uri,header,body等信息放到options对象中
rp(options).then (function (parseBody) {
console.log(parseBody)
}).catch(function(error){console.log("call api error" + error)})
// 使用rp.(options)
// .then(function(responseBody) {})
// .catch(function(error) {}) 完成接口调用,并获取接口的response中的内容,catch是可选的,如果需要捕获异常则添加上catch语句
}
function callPostApi2() {
const option= {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: {"name": "noBody"},
json: true
};
const uri="http://localhost:9090/api/addBookWithAnyBody/abc";
rq(uri,option,function(error,response,body) {
console.log(response);
console.log(body);
console.log(response.headers);
})
//上面是调用接口第二种写法,用request对象完成接口调用,另外调用接口的时候除了能获取response body,还可以获取response对象下的header等信息
}
function callGetApi() {
const options= {
method:"GET",
uri:"http://localhost:9090/api/getBookResponse/abc",
headers: {
"Content-Type": "application/json"
},
json: true
};
rp(options).then(function(parseBody){
console.log(parseBody);
console.log(parseBody.books[0].name)
}).catch(function(error) {
})
}
//上面是调用Get接口的案例,get方法的接口和post方法的接口很像,这里不再单独进行讲解
callPostApi();
callGetApi();
callPostApi2();
//这里是调用上面的funciton
运行上面的案例,可以看到打印了对应的response信息,说明整个接口调用成功。
上面都是单个接口的调用,实际应用场景中,更多的是多个接口调用。例如先调用登陆接口获取token,登陆接口返回的token作为其他接口request header中的值,再调用其他接口完成数据准备工作。
例如前面使用的web应用“ Conduit ”。该应用上可以创建帖子,然后对帖子进行评价。假设自动化测试场景要验证“能正常对帖子进行评价”,那么运行此场景前,需要先准备一个帖子,准备帖子就属于数据准备阶段,那么如何通过调用接口完成帖子创建呢?请看下面的案例,同样执行“npm run call-multiple-api"即可运行下面的案例。
const rp= require('request-promise');
async function createArticle(username, password) {
await getToken(username, password).then(token => {
// 调用包好的getToken()方法获取token信息,并提供给”创建帖子“接口使用,如果这里传入的token不正确,接口调用会报401错误
const option = {
method: "POST",
uri : "https://conduit.productionready.io/api/articles/",
headers: {
"Content-Type": "application/json",
"Authorization": 'Token ' + token
// 这里需要注意一点,接口的request header中Authorization值是Token+空格+登陆生成的token值
},
body: {"article":{"tagList":[],"title":"newArticle","description":"newArticle","body":"newArticle"}},
json: true
};
rp(option).then(function(parseBody) {
console.log(parseBody.article.title);
console.log(parseBody.article.author.username);
//调用”创建帖子“的接口后,打印response的值,以此判断接口调用是否真正成功
})
})
}
async function getToken(username, password) {
const option= {
method: "POST",
uri: "https://conduit.productionready.io/api/users/login",
headers: {
"Content-Type": "application/json"
},
body: {"user":{"email":username,"password":password}},
json: true
};
return rp(option).then(function(parseBody) {
return parseBody.user.token
})
}
//这里是调用登陆接口,并返回token信息,供其他接口使用
const rs = createArticle("e2etest@163.com","12345678");
//调用包好的“创建帖子”方法,这里登陆用的用户名、密码通过参数方式传入,没有写死在代码中
运行上面的案例,运行结果如下所示,可以看到打印了期望的信息,大家在执行此案例时也可以登陆应用,查看是否真正创建了对应的帖子。上面的的方法包好后,在“评价帖子“的自动化案例中,就可在before()中调用”createArticle()”生成测试数据,然后通过页面操作完成评价帖子的场景验证。
上面的例子中,登陆的用户信息是通过参数方式传入createArticle()中,创建帖子的帖子名称,帖子内容等是写死在代码中的,另外调用接口时使用的应用uri也是写死在代码中的。真实项目中,创建帖子时输入的帖子名称、内容,登陆的用户信息都属于静态测试数据范畴,需要用文件管理起来,当准备测试场景数据时从文件中读取即可。用此方式管理测试数据的优势是:如果随着项目变化,测试数据需要改变,那么只需修改文件中的内容即可,无需修改所有使用了用户登陆信息的所有代码。
另外,调用接口使用的uri属于配置信息范畴,当测试环境切换后,uri肯定会改变,配置信息也需要放到文件中管理起来,这样当环境切换后,根据切换的环境值调用对应的uri,这样才能保证自动化脚本能在多环境中切换运行。
关于如何实现测试数据和配置信息的管理将在后面章节进行详细讲解,当学习完测试数据和配置信息管理后,再来重新优化“评价帖子“测试场景中数据准备部分。