https://testerhome.com/topics/15301
https://testerhome.com/topics/18568
https://testerhome.com/topics/18514
https://github.com/reese0329/serviceTesting
公司的接口一直不稳定影响用户使用(测试痛点)
最近半年来出现了几次大的故障(质量反馈)
每次升级都会影响老功能(回归测试)
目前公司没有可靠的测试体系(测试策略)
微服务话改造要求有良好的测试体系保证(重构测试)
追查公司一来所有的故障原因,定位问题起因
访谈CTO、产品经理、研发、测试、运维收集质量痛点
分析业务架构与流程调用
分析监控系统了解业务使用数据
待测业务范围
待测业务场景用例
待测业务接口分析
业务的输入
业务流程
业务输出
静态分析方法
动态分析方法:
Python
java
go kotlin
尽量使用与研发一直的编程语言和技术栈
Java + RestAssured + JUnit/TestNG
Python + Requests + PyTest
Python + HttpRunner
.生成用例 + 编写用例
用例管理
.通用业务对象封装
.数据引用
.用例调试
.用例执行策略
.不同测试环境的切换
.触发执行
.执.记录
.结果报表
.报警机制
.根据功能覆盖不断迭代用例
.业务覆盖导向
.功能覆盖导向
.输入覆盖导向
.覆盖率导向
.集成测试有QA负责
.模块级别的单元测试由研发负责
.建设mock系统
.监控更多接口质量数据
.建立全流程的质量保证体系
.从线上数据建模用户行为
.利用线上用户行为反补用例覆盖
企业微信接口测试实战
第一部分实战:
接口测试用例实现
第二部分实战:
测试框架改进与智能化测试
添加依赖
com.jayway.jsonpath
json-path
2.4.0
import com.jayway.jsonpath.JsonPath;
package com.ace.wework;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;
import java.util.HashMap;
import static io.restassured.RestAssured.given;
public class Restful {
HashMap query=new HashMap();
public RequestSpecification requestSpecification=given();
public Response send(){
requestSpecification=given().log().all();
query.entrySet().forEach( entry-> {
requestSpecification.queryParam(entry.getKey(), entry.getValue());
});
return requestSpecification.when().request("get","baidu.com");
}
}
package com.ace.wework.contact;
import com.ace.wework.Restful;
import com.ace.wework.Wework;
import static io.restassured.RestAssured.given;
public class Contact extends Restful {
public Contact(){
reset();
; }
//重置
public void reset() {
requestSpecification
.log().all()
.param("access_token", Wework.getToken())
.expect().statusCode(200);
}
}
使用map模型化数据
封装
public Response create(HashMapmap){
reset(); //reset 清空数据
DocumentContext documentContext=JsonPath.parse(this.getClass().getResourceAsStream("/data/create.json"));
map.entrySet().forEach(entry->{
documentContext.set(entry.getKey(),entry.getValue());
});
return requestSpecification
.body(documentContext.jsonString())
.when().post("https://qyapi.weixin.qq.com/cgi-bin/department/create")
.then().extract().response();
}
@Test
void create1() {
HashMapmap=new HashMap(){
{put("name","test"+random);
put("parentid","1");}
};
department.create(map).then().body("errcode",equalTo(0));
}
0317_下午2
13:28
创建成员
创建模板-restful 读取jsonPath模板
public static String template(String path, HashMapmap){
DocumentContext documentContext= JsonPath.parse(Restful.class
.getResourceAsStream(path));
map.entrySet().forEach(entry->{
documentContext.set(entry.getKey(),entry.getValue());
});
return documentContext.jsonString();
方法封装
package com.ace.wework.contact;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import io.restassured.response.Response;
import java.util.HashMap;
import java.util.Objects;
public class Member extends Contact{
public Response create(HashMapmap){
String body=template("/data/Member.json",map);
reset();
return requestSpecification
.body(body)
.when().post("https://qyapi.weixin.qq.com/cgi-bin/user/create")
.then().log().all().extract().response();
}
}
封装测试用例
package com.ace.wework.contact;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.*;
class MemberTest {
static Member member;
@BeforeAll
static void setup(){
member=new Member();
}
@Test
void create() {
HashMap map= new HashMap<>();
map.put("userid","test0617"+member.random);
map.put("name","test0617"+member.random);
map.put("alias","test0617"+member.random);
map.put("mobile","151"+member.random.substring(0,8));
map.put("email",member.random.substring(0,8)+"@qq.com");
map.put("department", Arrays.asList(1,2));
member.create(map).then().statusCode(200).body("errcode",equalTo(0));
}
}
测试数据
{
"userid": "zhangsan",
"name": "张三",
"alias": "jackzhang",
"mobile": "15913215421",
"department": [1, 2],
"order":[10,40],
"position": "产品经理",
"gender": "1",
"email": "[email protected]",
"is_leader_in_dept": [1, 0],
"enable":1,
"avatar_mediaid": null,
"telephone": "020-123456",
"address": "广州市海珠区新港中路",
"extattr": {
"attrs": [
{
"type": 0,
"name": "文本名称",
"text": {
"value": "文本"
}
},
{
"type": 1,
"name": "网页名称",
"web": {
"url": "http://www.test.com",
"title": "标题"
}
}
]
},
"to_invite": true,
"external_position": "高级产品经理",
"external_profile": {
"external_corp_name": null,
"external_attr": [
{
"type": 0,
"name": "文本名称",
"text": {
"value": "文本"
}
},
{
"type": 1,
"name": "网页名称",
"web": {
"url": "http://www.test.com",
"title": "标题"
}
},
{
"type": 2,
"name": "测试app",
"miniprogram": {
"appid": "wx8bd80126147df384",
"pagepath": "/index",
"title": "waimai"
}
}
]
}
}
添加依赖
org.junit.jupiter
junit-jupiter-params
5.3.2
MemberTest.java
抽象userid为参数
package com.ace.wework.contact;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import java.util.Arrays;
import java.util.HashMap;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.jupiter.api.Assertions.*;
class MemberTest {
static Member member;
@BeforeAll
static void setup(){
member=new Member();
}
@ParameterizedTest
@ValueSource(strings ={"tester","ace"})
void create(String name) {
String nameNew=name+member.random;
String random=String.valueOf(System.currentTimeMillis()).substring(5+0,5+8);
HashMap map= new HashMap<>();
map.put("userid",name+member.random);
map.put("name",name+member.random);
map.put("alias","test0617"+member.random);
map.put("mobile","151"+random);
map.put("email",random+"@qq.com");
map.put("department", Arrays.asList(1,2));
member.create(map).then().statusCode(200).body("errcode",equalTo(0));
}
}
删除全部部门
https://github.com/rest-assured/rest-assured/wiki/Usage#json-schema-validation
public Response deleteAll(){
reset();
List idList=list("").then().extract().path("department.id");
//将部门列表转换成列表形式
System.out.println(idList);
idList.forEach(id->delete(id.toString()));
return null;
}
@Test
void deleteAll() {
department.deleteAll();
}
优化:
@BeforeEach
void setUp() {
if (department == null) {
department = new Department();
department.deleteAll();
}
}
加载数据
执行顺序
case管理
封装department的list,搜索特定id的部门,并在测试用例中断言i部门的d是否正确,把你的list的封装和测试用例代码贴到回复里
封装部门更新与删除,实现添加部门并删除部门 delete(),添加部门并更新部门 update() 贴update的封装与测试用例代码
封装
public Response update(String name,String id){
String body = JsonPath.parse(this.getClass().getResourceAsStream("/data/update.json"))
.set("$.name",name)
.set("$.id",id).jsonString();
return given().log().all().queryParam("access_token",Wework.getToken())
.body(body)
.when().post("https://qyapi.weixin.qq.com/cgi-bin/department/update")
.then().log().all().statusCode(200).extract().response();
}
public Response delete(String id){
return given().log().all().queryParam("access_token",Wework.getToken())
.queryParam("id",id)
.when().get("https://qyapi.weixin.qq.com/cgi-bin/department/delete")
.then().log().all().extract().response();
}
update.json
{
"id": 6,
"name": "广州研发中心",
"parentid": 1,
"order": 1
}
测试用例
不依赖其他数据 修改或更新前先创建部门
@Test
void update() {
String nameOld="HR_18";
department.create(nameOld, "0616");
String id=String.valueOf(department.list("").path("department.find{ it.name=='"+ nameOld +"'}.id"));
department.update("HR061613", id)
.then().body("errcode", equalTo(0));
}
@Test
void delete()
// {
// department.create("python", "190616")
// .then().body("errcode", equalTo(0));
// department.delete("190616")
// .then().body("errcode", equalTo(0));
// }
{
String nameOld ="python2";
department.create(nameOld, "1906162");
String id = String.valueOf(department.list("").path("department.find{ it.name=='" + nameOld + "'}.id"));
department.delete(id)
.then().body("errcode", equalTo(0));
}
或生成时间戳生成测试用例
@Test
void update() {
String nameOld="HR_18"+random;
department.create(nameOld);
String id=String.valueOf(department.list("").path("department.find{ it.name=='"+ nameOld +"'}.id"));
department.update("HR061613"+random, id)
.then().body("errcode", equalTo(0));
}
此处修改create方法中的参数只有name
此方法会产生很多的脏数据,注意处理
解决创建时,部门名称为中文的问题
随机数据
class DepartmentTest {
Department department;
String random=String.valueOf(System.currentTimeMillis());
@BeforeEach
void setUp() {
if (department == null) {
department = new Department();
}
}
@Test
void list() {
department.list("").then().statusCode(200)
.body("department.name[0]", equalTo("ace"));
// department.list("39").then().statusCode(200)
// .body("department.name[0]",equalTo("ningningCenterdd"))
// .body("department.id[0]",equalTo(39));
}
@Test
void create() {
department.create("测试"+random)
.then().body("errcode", equalTo(0));
}
完成成员管理的所有封装与测试用例,把自己的代码开源道github,并回帖自己的github地址
封装自己的读取yaml或者读取har格式并发送http请求的方法 templateFromHar or templateFromYaml,更新到github并贴地址。注明是哪个作业。比如
作业4 github文件地址
测试用例辅助设计
右键copy all as fetch
创建*.har.json文件
从html中读取测试内容
Restful中读取
public Response templateFromHar(String path,String pattern, HashMap map){
//从har中读取请求,进行更新
DocumentContext documentContext = JsonPath.parse(Restful.class
.getResourceAsStream(path));
map.entrySet().forEach(entry-> {
documentContext.set(entry.getKey(), entry.getValue());
});
String method=documentContext.read("method:");
String url=documentContext.read("url");
return requestSpecification.when().request(method,url);
}
Department中调用
public Response update(HashMap map){
return templateFromHar("demo.har.json","https://work.weixin.qq.com/wework_admin/party?action=addparty",map);
}
Swagger
https://petstore.swagger.io/?_ga=2.107822722.1416948099.1562839798-910621538.1562839798
https://petstore.swagger.io/v2/swagger.json
保存至