• docker 实战 at April 20, 2020

    课堂作业 一
    1、使用docker搭建一个可以访问的jenkins服务
    http://192.168.99.100:8080/

    2、普罗米修斯

    3、grafan

  • 命令行运行

    一、运行时使用代理插桩javaagent及报告生成
    1、生成jacoco.exec文件
    java -javaagent:/Users/liuqi/Documents/apache-jmeter-5.2.1/lib/ext/coverage/org.jacoco.agent-0.8.5-runtime.jar -jar /Users/liuqi/Documents/apache-jmeter-5.2.1/bin/ApacheJMeter.jar

    2、将jacoco.exec文件生成HTML报告
    1)无源码报告
    java -jar /Users/liuqi/Documents/apache-jmeter-5.2.1/lib/ext/coverage/org.jacoco.cli-0.8.5-nodeps.jar report jacoco.exec --classfiles /Users/liuqi/Documents/apache-jmeter-5.2.1/bin/ApacheJMeter.jar --html report/

    2)有源码报告
    java -jar org.jacoco.cli-0.8.6-20200327.115542-43-nodeps.jar report jacoco.exec --classfiles "/Users/liuqi/Documents/apache-jmeter-5.2.1/bin/ApacheJMeter.jar" --html report/ --sourcefiles /Users/liuqi/Documents/github/jmeter/src/launcher/src/main/java/

    IDEA使用maven pom.xml文件配置jacoco信息:
    参考https://testerhome.com/topics/21072此帖子

  • 作业Git地址:https://github.com/liuqiceshi/XUnit
    作业1
    //todo: 获取部门成员列表

    testcase:
    @DisplayName("获取部门成员")
    @Test
    public void simplelist(){
    int department_id=2;
    HashMap<String,Object> data=new HashMap<>();
    data.put("department_id",department_id);
    Users users=new Users();
    users.simplelist(data).then().body("errcode",equalTo(0));
    }

    api:
    //获取部门成员
    public Response simplelist( HashMap<String, Object> data) {

    String body=templateTest.template("/service/user/api/simplelist.json",data);
    //转化为map类型
    JSONObject jsonObject = JSON.parseObject(body);
    return given()
    .queryParam("access_token",Work.getInstance().getToken())
    .queryParams(jsonObject)
    .when().log().all()
    .post(Work.baseUrl+Work.simplelistPath)
    .then().log().all().extract().response();
    }

    simplelist.json
    {
    "department_id": "{{department_id}}",
    "fetch_child": "1"
    }

    //todo: 批量删除成员

    testcase:
    @DisplayName("批量删除成员batchdelete")
    @Test
    public void batchdelete(){
    //todo
    Users users=new Users();
    ArrayList<String> useridlist=new ArrayList<>();
    //先批量创建用户
    for (int i = 0; i <5 ; i++) {
    String userid="userid"+System.currentTimeMillis();
    String name="liuqi"+System.currentTimeMillis();
    HashMap<String,Object> data=new HashMap<>();
    data.put("userid",userid);
    data.put("name",name);
    data.put("mobile",String.valueOf(System.currentTimeMillis()).substring(0,11));
    data.put("email",String.valueOf(System.currentTimeMillis()).substring(0,11));
    //调用模板数据
    users.clone(data).then().body("errcode",equalTo(0));
    //调用用户列表
    users.getUseList(userid).then().body("errcode",equalTo(0));
    //添加到list中
    useridlist.add(userid);
    }
    //将list放在map中,进行批量删除用户
    HashMap<String,Object> data=new HashMap<>();
    data.put("useridlist",useridlist);
    //批量删除
    int department_id=2;
    users.batchdelete(data).then().body("errmsg",equalTo("deleted"));
    users.simplelist(department_id).then().body("errcode",equalTo(0));
    }

    api:
    // 批量删除成员
    public Response batchdelete(HashMap<String,Object> data) {

    return given()
    .queryParam("access_token",Work.getInstance().getToken())
    .body(data)
    .when().log().all()
    .post(Work.baseUrl+Work.batchdeletePath)
    .then().log().all().extract().response();

    }

    作业2:
    自己完成对get接口的po数据驱动

    UsersTest:
    public class UsersTest {

    /**
    * 用CsvSource进行数据驱动
    * @param userid
    */

    @DisplayName("getUseListPo方法")
    @ParameterizedTest
    @CsvSource({
    "xiaolidev1",
    "productTest"
    })
    public void getUseListPo(String userid){
    if(userid.isEmpty()){
    userid="liuqi"+System.currentTimeMillis();

    }
    Users users=new Users();
    users.getUseListPO(userid).then().body("errmsg",equalTo("ok"));
    }
    }

    BaseApi:
    package service.user.api;

    import com.fasterxml.jackson.databind.DeserializationFeature;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
    import framework.ApiObjectModel;
    import io.restassured.response.Response;
    import org.junit.jupiter.api.Test;

    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;

    /**
    * @Author liuqi
    * @Date 20/1/17 14:42
    * @Version 1.0
    * @Description:
    */

    public class BaseApi {

    ApiObjectModel apiObjectModel=new ApiObjectModel();
    HashMap<String, Object> params;

    //获取需要替换的参数
    public void setParams(HashMap<String, Object> datas) {
    params=datas;
    }

    public Response parseSteps(){
    String method2=Thread.currentThread().getStackTrace()[2].getMethodName();
    System.out.println("method2=========:"+method2);

    if (apiObjectModel.methods.entrySet().isEmpty()){
    System.out.println("yaml first laod");

    String path = "/" + this.getClass().getCanonicalName().replace('.', '/') + ".yaml";
    ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
    //mapper序列化设置为false
    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    try {
    // System.out.println("=========apiObjectModel前=======");
    // System.out.println( "liuqi=========="+BaseApi.class.getResourceAsStream(path).toString());
    InputStream is = BaseApi.class.getResourceAsStream(path);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int i;
    while ((i = is.read()) != -1) {
    baos.write(i);
    }
    String str = baos.toString();
    System.out.println("yaml文件内容:===="+str);
    apiObjectModel = mapper.readValue(BaseApi.class.getResourceAsStream(path), ApiObjectModel.class);
    // System.out.println("=========apiObjectModel后=======");
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    return (Response) apiObjectModel.run(method2,params);



    // String className=Thread.currentThread().getStackTrace()[2].getClassName();
    // System.out.println("==================className=========:"+className);
    // String method0=Thread.currentThread().getStackTrace()[0].getMethodName();
    // System.out.println("==================method0=========:"+method0);
    // String method1=Thread.currentThread().getStackTrace()[1].getMethodName();
    // System.out.println("==================method1=========:"+method1);
    // String method2=Thread.currentThread().getStackTrace()[2].getMethodName();
    // System.out.println("==================method2=========:"+method2);
    // String method3=Thread.currentThread().getStackTrace()[3].getMethodName();
    // System.out.println("==================method3=========:"+method3);
    // String method4=Thread.currentThread().getStackTrace()[4].getMethodName();
    // System.out.println("==================method4=========:"+method4);
    // String method5=Thread.currentThread().getStackTrace()[5].getMethodName();
    // System.out.println("==================method5=========:"+method5);



    }


    //getStackTrace学习
    @Test
    public void stepsTest(){
    System.out.println("我是stepsTest方法");
    steps();

    }
    public void steps(){
    System.out.println("我是steps方法");
    parseSteps();
    }


    }

    ApiObjectModel
    package framework;


    import io.restassured.response.Response;

    import java.util.HashMap;

    /**
    * @Author liuqi
    * @Date 20/1/17 14:39
    * @Version 1.0
    * @Description:
    */

    public class ApiObjectModel {
    //PO模型
    public HashMap<String, ApiMethodModel> methods = new HashMap<>();

    public ApiMethodModel getMethod(String method) {
    return methods.get(method);
    }

    /**
    * run方法只有method参数
    * @param method
    * @return
    */

    public Response run(String method) {
    /* if (methods != null && methods.size() > 0) {
    System.out.println("url=======:"+methods.get(method).url);
    } else {
    System.out.println("methods为空");
    }
    System.out.println("打印method========"+method);*/

    return getMethod(method).run();
    }


    /**
    * run方法 method和params
    * @param method2
    * @param params
    * @return
    */

    public Object run(String method2, HashMap<String, Object> params) {
    return getMethod(method2).run(params);

    }
    }


    ApiMethodModel:
    package framework;

    import io.restassured.response.Response;
    import io.restassured.specification.RequestSpecification;
    import org.junit.jupiter.api.Test;
    import service.Work;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;

    import static io.restassured.RestAssured.given;

    /**
    * @Author liuqi
    * @Date 20/1/17 14:41
    * @Version 1.0
    * @Description:
    */

    public class ApiMethodModel {
    public HashMap<String, Object> query;
    public HashMap<String, Object> header;
    public HashMap<String, Object> postBody;
    public String postBodyRaw;
    //pos get
    public String method = "get";
    public String url;
    public HashMap<String, Object> params;

    public Response run() {
    RequestSpecification request = given();
    request.queryParam("access_token", Work.getInstance().getToken());

    if (query != null) {
    Set<HashMap.Entry<String, Object>> entries = query.entrySet();
    for (HashMap.Entry<String, Object> entry : entries) {
    request.queryParam(entry.getKey(), replace(entry.getValue().toString()));
    }
    }
    if (header != null) {
    Set<HashMap.Entry<String, Object>> entries = header.entrySet();
    for (HashMap.Entry<String, Object> entry : entries) {
    request.header(entry.getKey(), replace(entry.getValue().toString()));
    }
    }
    if (postBody!=null){
    //todo replace HashMap 递归算法替换参数
    request.body(postBody);
    }
    if(postBodyRaw!=null){
    request.body(replace(postBodyRaw));
    }
    System.out.println("request============:"+request);
    return request
    .when().log().all()
    .request(method,url)
    .then().log().all()
    .extract().response();
    }
    /**
    * 处理需要替换的参数
    * @param params
    * @return
    */

    public Object run(HashMap<String, Object> params) {
    this.params=params;
    return run();

    }

    /**
    * 替换yaml文件中变量值{{}}
    * @param raw
    * @return
    */

    public String replace(String raw){

    Set<HashMap.Entry<String, Object>> entries = params.entrySet();
    for (HashMap.Entry<String, Object> entry : entries) {
    String matcher="${"+entry.getKey()+"}";
    if (raw.contains(matcher)){
    System.out.println("matcher========"+matcher);
    System.out.println("raw==========="+raw);
    raw=raw.replace(matcher,entry.getValue().toString());
    System.out.println("raw==========="+raw);
    }
    }

    return raw;
    }




    @Test
    public void HashMapTest(){
    //map
    Map<Integer,String> map =new HashMap();
    map.put(1,"zhangsan");
    map.put(2,"lisi");
    map.put(3,"wangwu");
    map.put(4,"zhangsan");
    //通过entrySet获取key和value的值
    Set<Map.Entry<Integer, String>> entries=map.entrySet();
    for(Map.Entry<Integer,String> entry:entries){
    System.out.println("key值:"+entry.getKey()+"value值:"+entry.getValue());

    }
    }
    }

    Users.yaml:
    methods:
    getUseListPO:
    query:
    userid: ${userid}
    method: get
    url: https://qyapi.weixin.qq.com/cgi-bin/user/get



    作业3 :给更新方法进行方法驱动

    /**
    * 通过HashMap修改多个字段值,通过MethodSource进行数据驱动
    * @param userid
    * @param name
    */

    @DisplayName("更新用户")
    @ParameterizedTest
    @MethodSource("updateFormYamlDatas")
    public void update(String userid,String name){
    service.user.api.Users users=new service.user.api.Users();
    String addressNew="地球上666";
    HashMap<String,Object> data=new HashMap();
    data.put("name",name);
    data.put("address",addressNew);
    users.update(userid,data);
    users.getUseList(userid)
    .then()
    .body("name",equalTo(name))
    .body("address",equalTo(addressNew));

    }

    //通过流数据驱动
    public static Stream<Arguments> updateFormYamlDatas(){
    ObjectMapper objectMapper=new ObjectMapper(new YAMLFactory());
    TypeReference<List<HashMap<String,Object>>> typeReference=new TypeReference<List<HashMap<String,Object>>>(){};
    List<HashMap<String,Object>> datas;
    try {
    //读取yaml文件
    datas=objectMapper.readValue(UsersTest.class.getResourceAsStream("updateUers.yaml"),typeReference);
    //获取各个字段添加到流中
    ArrayList<Arguments> results =new ArrayList<Arguments>() ;
    for (HashMap<String,Object> data:datas) {
    results.add(
    Arguments.arguments(data.get("userid"),data.get("name"))
    );

    }
    return results.stream();
    } catch (IOException e) {
    e.printStackTrace();
    }

    return Stream.of(
    // Arguments.arguments("xiaolidev1","小李的新名字TestArguments")
    );

    }

    updateUers.yaml
    - userid: wangwu
    name: wangwuUserIdYamlYaml
    - userid: productTest
    name: 产品1YamlYaml
  • @Test
    public void testBase64Test(){
    given().filter((req, res, ctx) -> {
    Response resOrigin = ctx.next(req, res);

    // String bodyNew = resOrigin.getBody().asString().replace("\"userid\": \"seveniruby\",", "\"userid\": null,");
    ResponseBuilder responseBuilder = new ResponseBuilder().clone(resOrigin);

    String bodyNew=new String(
    Base64.getDecoder().decode(
    resOrigin.body().asString().trim()
    )
    );

    responseBuilder.setBody(bodyNew);
    return responseBuilder.build();
    })
    .when().log().all().get("http://101.132.159.87:8080/user.json")
    .then().log().all().statusCode(200);

    }

  • 1、API自动化用例怎么应对项目中快速迭代?
    2、在restassured的API框架基础上怎么做成平台页面化的?(做成页面化主要给让测试新手、产品、运营使用);

  • SwitchyOmega Chrome Proxy工具安装:
    1、官网下载SwitchyOmega_Chromium.crx https://github.com/FelisCatus/SwitchyOmega/releases
    2、将SwitchyOmega_Chromium.crx改为SwitchyOmega_Chromium.zip 然后用命令解压到任意目录 unzip SwitchyOmega_Chromium.zip -d /Users/zhangsan/Downloads/SwitchyOmega_Chromium
    3、在Chrome 更多工具——>扩展程序——>打开开发者模式——>点击按钮 【已加载扩展程序】然后选择2中已经解压好的文件目录就可以

    作业1:
    pc:

    ios:

    rewrite规则

    作业2 创建一个以自己的名字命名的部门,编写用例,并把用例贴到回复里。

    package service;


    import io.restassured.http.ContentType;
    import org.junit.jupiter.api.BeforeAll;
    import org.junit.jupiter.api.Test;

    import java.util.HashMap;

    import static io.restassured.RestAssured.given;
    import static org.hamcrest.Matchers.equalTo;

    /**
    * @Author liuqi
    * @Date 19/12/28 14:40
    * @Version 1.0
    * @Description:
    */

    public class TestWork {
    //https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=ID&corpsecret=SECRET
    //公共地址
    static String baseUrl="https://qyapi.weixin.qq.com/cgi-bin";
    static String corpid="wwba4073edf8ccf28d";
    static String corpsecret="MyNy688YygBDO39LP7H95E-Cqp-Z3_AVs5sR0d4fdwA";
    static String access_token;
    //获取access_token路径
    static String gettokenPath="/gettoken";
    //创建部门路径
    static String createDepartPath="/department/create";
    //
    static String parentid="4";

    //获取access_token
    @BeforeAll
    public static void getToken(){
    access_token= given()
    .param("corpid",corpid)
    .param("corpsecret",corpsecret)
    .when().log().all()
    .get(baseUrl+gettokenPath)
    .then().log().all()
    .body("errcode",equalTo(0))
    .body("errmsg",equalTo("ok"))
    .extract()
    .body().path("access_token");
    System.out.println("token++++++"+access_token);

    }


    @Test
    public void departCreate(){
    String name="api自动创建部门刘琦"+System.currentTimeMillis();
    HashMap<String,Object> data=new HashMap<>();
    data.put("name",name);
    data.put("parentid",parentid);

    given()
    .queryParam("access_token",access_token)
    .contentType(ContentType.JSON)
    .body(param)
    .when().log().all()
    .post(baseUrl+createDepartPath)
    .then().log().all()
    .body("errcode",equalTo(0))
    .body("errmsg",equalTo("created"));
    }
    }


    作业2遇到问题(业务设置问题):
    {
    "errcode": 48002,
    "errmsg": "api forbidden, hint: [1578286446_54_fa152ccc64821285fd5ae516d090a010], from ip: 101.254.182.38, more info at https://open.work.weixin.qq.com/devtool/query?e=48002"
    }

    解决办法:管理工具——>通讯录同步——>权限,选择(API编辑通讯录)就可以了

    作业3 :把标签管理的完整PO模型设计好,并编写测试用例。代码上传到你的个人github,并把链接贴到回复里

    https://github.com/liuqiceshi/XUnit/tree/master/src/test/java/service/department

  • https://github.com/liuqiceshi/XUnit
    一、课间作业
    编写一个应用代码类
    编写测试用例
    完善你的代码
    利用IDEA统计覆盖率
    1、使用maven完成覆盖率
    2、直接贴jacoco的代码覆盖率截图,参考上图

    mvn clean test -Dtest=LoginCoverageTest -Dmaven.test.failure.ignore=true verify

    二、课后作业
    针对selenium 或者 restassred,针对其中的若干类,执行单元测试,要求包含基本的单测,以及mock应用。
    把其中一个mock相关的类文件内容贴到回复里

  • 一、junit4执行顺序
    i am @BeforeClass
    I am @Before
    i am testDemo1
    I am @Aefter
    I am @Before
    i am testDemo2
    I am @Aefter
    I am @Before
    i am testDemo3
    I am @Aefter
    i am @AfterClass

    二、继承的时候执行顺序
    i am @BeforeClass
    i am children @BeforeClass
    I am @Before
    i am children @Before
    i am children testDemo1
    i am children @Aefter
    I am @Aefter
    I am @Before
    i am children @Before
    i am children testDemo3
    i am children @Aefter
    I am @Aefter
    I am @Before
    i am children @Before
    i am children testDemo2
    i am children @Aefter
    I am @Aefter
    I am @Before
    i am children @Before
    i am testDemo1
    i am children @Aefter
    I am @Aefter
    I am @Before
    i am children @Before
    i am testDemo2
    i am children @Aefter
    I am @Aefter
    I am @Before
    i am children @Before
    i am testDemo3
    i am children @Aefter
    I am @Aefter
    i am children @AfterClass
    i am @AfterClass

  • liuqiceshi
    作业:

    gitlab创建项目liuqipoject3  http://47.95.238.18:10080/liuqi/liuqipoject3默认有master 分支
    克隆到本地:git clone git clone ssh://git@47.95.238.18:10022/liuqi/liuqipoject3.git
    修改master Readme 文件 提交2
    git add README.md
    git commit -m "master init”
    git push --set-upstream origin master
    git add README.md
    git commit -m "
    master 1th
    git push --set-upstream origin master

    创建release develop 分支
    git branch release
    git checkout -b develop
    git checkout -b feature-liuqi-20190924

    创建 release分支下提交2
    git checkout -b release
    git add README.md
    git commit -m "release 1th”
    git push --set-upstream origin release

    git add test.txt
    git commit -m "
    test.txt
    git push --set-upstream origin release

    develop分支提交2
    git add README.md
    git commit -m "develop 1th"
    git push --set-upstream origin develop

    git add README.md
    git commit -m "develop 2th"
    git push --set-upstream origin develop


    由于现在master releasedevelop在同一条线上需要给masterrelease添加内容使不在一条线上
    master添加:
    git add master.txt
    git commit -m "add master.txt”
    git push origin master

    release添加:
    git add release.txt
    git commit -m "
    add release.txt
    git push --set-upstream origin release


    develop下建 feature-liuqi-20190925 分支下边提交

    git add feature1.txt
    git commit -m "add feature1.txt"
    git push --set-upstream origin feature-liuqi-20190925

    解决developfeature同一条线上,在develop中添加develop2.txt
    git add develop2.txt
    git commit -m develop2.txt
    git push --set-upstream origin develop

    进入develop下进行merge feature分支:
    git merge origin feature-liuqi-20190924
    git add feature1.txt
    git commit -m feature merger into develop
    git push --set-upstream origin develop

    进入release下进行merge develop分支:
    git merge origin develop
    git add develop1.txt develop2.txt feature1.txt master.txt
    git commit -m "develop merge into release
    git push --set-upstream origin release

    gitlabrelease 合并到master分支上

    在gitlab pr时遇到的问题:

    点击 Edit 然后选中要改变状态的框,修改状态 ,点击 update all 按钮 之后就可以从新发起一个新的pr

  • 一、创建项目:
    echo "# 20190918Test" >> README.md
    git add README.md
    git commit -m "first commit"
    git remote add origin https://github.com/liuqiceshi/20190918Test.git
    git push -u origin master

    二、fork别人项目
    原分支:
    自己分支:https://github.com/liuqiceshi/Hogwarts_Online2/tree/master