接口测试用例数据驱动&高级断言_rest_assured

https://testerhome.com/topics/15161

https://github.com/reese0329/rest_assured

idea 引入包 Alt+Enter

 

 

参考

https://testerhome.com/topics/11731

接口测试大纲

接口测试的价值

移动互联网公司的一般架构简化模拟

接口测试用例数据驱动&高级断言_rest_assured_第1张图片

真实的后端服务

 

接口测试的必要性

行业成熟方案
更早的发现问题
更快的质量反馈

 

接口测试用例数据驱动&高级断言_rest_assured_第2张图片

 

 

 

接口测试不能做什么

接口测试不能解决移动端的质量,仍然需要端的测试

 

 

接口基础知识

接口为观测与分析

接口例编写与管理

接口测试的运行维护

接口的监控分析

 

接口测试流程

  1. 接口的范围:需要覆盖多少业务和接口  模块
  2. 接口分析:接口的协议、上下游依赖
  3. 接口测试用例设计:业务用例如何模拟和覆盖  技术 测试业务覆盖
  4. 接口测试框架选择:选择合适的框架
  5. 测试用例编写与维护: 例编写与维护更新
  6. 持续集成:不断集成测试

 

待测接口范围

业务需求调研:研发和产品反馈常出问题的业务  Charles 抓包

接口文档:人工档、swagger自动生成的文档

代码分析:分析spring等框架的代码

线上log和数据:线上的生成监控和接 log

客户端抓包:基于用户角度的接口行为分析

 

常见抓包分析

  监听分析:tcpdump+wireshark+har提取工具

  代理分析:charles+burpsuite

  转发分析:修改host域名+反向代理转发

 

测试用例设计

❖  接口调用的流程分析

❖  代理抓包

  线上log提取

❖  用例补充:用流程图和思维导图进行业务建模  覆盖参数 API  及API之间调用的顺序

  正常场景用例 right path

❖  异常场景用例

❖  安全和稳定性用例

 

接口测试框架选择

  • 早期阶段:基于各种语 的httpclient封装
  • JMeter:性能测试 具,不具备完备的接 测试框架功能
  • RobotFramework:强 的ATDD 具,不过约束性太
  • RestAssured + Swagger
  • SoapUI [商业化]

 

推荐开源的Rest-Assured

简约的接口测试DSL

 支持xml json的结构化解析

  支持xpath jsonpath gpath等多种解析 式

spring的支持比较全

 

接口测试用例编写

用例编写(演练)

  创建maven

  添加依赖 restassured + junit/testng

❖  编写用例

❖  添加断言

❖  调试

32‘’

基本请求(演练)

  基本请求:getpost、添加headerjson请求

  常见的结果:statusCodehtmlxmljsonjsonp

  用例调试:logproxy

 

 

given 用户给定的条件

when 标准的action 

then 断言

 

 

断言机制

常见断言

状态码

  • html验证

接口测试用例数据驱动&高级断言_rest_assured_第3张图片

 

验证网页标题,返回状态码

import java.util.concurrent.TimeUnit;

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

public class DoubanTest {

    @Test
    public void testGetHtml() {
        useRelaxedHTTPSValidation();  //不强行校验http信息
        given()
                .log().all()
                .when()
                .get("https://www.douban.com")
                .then()
                .log().all()
                .statusCode(200).body("html.head.title",equalTo("豆瓣"));
    }
}
  • Json断言

json jpath

https://static.javadoc.io/io.rest-assured/json-path/3.3.0/io/restassured/path/json/JsonPath.html

遵循 Groovy GPath 语法

对topic列表id断言

package restassured;

import org.junit.BeforeClass;
import org.junit.Test;

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

public class Testerhome {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }

    @Test
    public void topic(){
        given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json").prettyPeek()
                .then()
                .body("topics.id[0]",equalTo(18728));
    }
}


 

 

 List books = with(Object).get("store.book.findAll { book -> book.price >= 5 && book.price <= 15 }");

 

1‘’

package restassured;

import org.junit.BeforeClass;
import org.junit.Test;

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

public class Testerhome {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }

    @Test
    public void topic(){
        given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json").prettyPeek()
                .then()
                //获取第一个topic的id
//                .body("topics.id[0]",equalTo(18739))
                //获取最后一个topic的user的login信息
                .body("topics[-1].user.login",equalTo("cmlanche-github"))
                //获取id为18316的topic的title
                .body("topics.find{it.id==18316}.title",
                equalTo("[深圳] 微众银行内部人才招聘高级测试工程师 / 测试工程师"));   //页面修改
    }
}

 

equal to 依赖

.body(路径,匹配器)

public T body(String path, Matcher matcher, Object... additionalKeyMatcherPairs) {
    this.responseSpec.body(path, matcher, additionalKeyMatcherPairs);
    return (ValidatableResponseOptions)this;
}

属于org.hamcrest库,所有语言都可以用  python版本和java版本

http://hamcrest.org/

A tour of common matchers

Hamcrest comes with a library of useful matchers. Here are some of the most important ones.

Core

anything - always matches, useful if you don’t care what the object under test is

describedAs - decorator to adding custom failure description

is - decorator to improve readability - see “Sugar”, below

Logical

allOf - matches if all matchers match, short circuits (like Java &&)

anyOf - matches if any matchers match, short circuits (like Java   )

not - matches if the wrapped matcher doesn’t match and vice versa

Object

equalTo - test object equality using Object.equals

hasToString - test Object.toString

instanceOfisCompatibleType - test type

notNullValuenullValue - test for null

sameInstance - test object identity

Beans

hasProperty - test JavaBeans properties

Collections

array - test an array’s elements against an array of matchers

hasEntryhasKeyhasValue - test a map contains an entry, key or value

hasItemhasItems - test a collection contains elements

hasItemInArray - test an array contains an element

Number

closeTo - test floating point values are close to a given value

greaterThangreaterThanOrEqualTolessThanlessThanOrEqualTo - test ordering

Text

equalToIgnoringCase - test string equality ignoring case

equalToIgnoringWhiteSpace - test string equality ignoring differences in runs of whitespace

containsStringendsWithstartsWith - test string matching

 

hasItems实例:

package restassured;

import org.junit.BeforeClass;
import org.junit.Test;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.useRelaxedHTTPSValidation;
import static org.hamcrest.Matchers.hasItems;

public class Json_Testerhome {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }

    @Test
    public void topic(){
        given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json").prettyPeek()
                .then()
                //获取第一个topic的id
//                .body("topics.id[0]",equalTo(18739))
                //获取最后一个topic的user的login信息
//                .body("topics[-1].user.login",equalTo("cmlanche-github"))
                //获取id为18316的topic的title
//                .body("topics.find{it.id==18316}.title",
//                equalTo("[深圳] 微众银行内部人才招聘高级测试工程师 / 测试工程师"))   //页面修改
                //hasItems实例
                .body("topics.id",hasItems(18316,18739));
    }
}

 

 

 

 

python:https://github.com/hamcrest/PyHamcrest

from hamcrest import *
import unittest

class BiscuitTest(unittest.TestCase):
    def testEquals(self):
        theBiscuit = Biscuit('Ginger')
        myBiscuit = Biscuit('Ginger')
        assert_that(theBiscuit, equal_to(myBiscuit))

if __name__ == '__main__':
    unittest.main()

 

assert_that(theBiscuit, equal_to(myBiscuit))

真实结果,期待结果

 

 

1'25

  • xml

https://static.javadoc.io/io.rest-assured/xml-path/3.3.0/io/restassured/path/xml/XmlPath.html

特有:

To get the number of categories with type attribute equal to 'groceries':

    int items = with(XML).get("shopping.category.findAll { it.@type == 'groceries' }.size()");

Get the chocolate price:

 int priceOfChocolate = with(XML).getInt("**.find { it.name == 'Chocolate' }.price"

 

 

用python创建一个简单的http服务

https://www.jianshu.com/p/94731886b28e

端口为8329

 

 

对于数量的断言,json也可用  .size()

//断言category的数量为3
.body("shopping.category.size()",equalTo(3))
//断言category中type为supplies的数量为1
.body("shopping.category.find{it.@type=='supplies'}.size()",equalTo(1))

it代表类目下

//断言category中type为supplies的第一个item中的price为5
.body("shopping.category.find{it.@type=='supplies'}.item[0].price",equalTo("5"))
package restassured;

import org.junit.Test;

import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.core.IsEqual.equalTo;

public class Xml_demo {
    @Test
    public void  testXML(){
        given().
                get("http://localhost:8329/demo.xml")
                .then()
                //断言category的数量为3
                .body("shopping.category.size()",equalTo(3))
                //断言category中type为supplies的数量为1
                .body("shopping.category.find{it.@type=='supplies'}.size()",equalTo(1))
                //断言category中type为supplies的第一个item中的price为5
                .body("shopping.category.find{it.@type=='supplies'}.item[0].price",equalTo("5"));

    }
}

 

1‘40

用例调试:logproxy

get("http://localhost:8329/demo.xml").prettyPeek()

.log().all() 可以打印全部请求

 

package restassured;

import org.junit.Test;

import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.core.IsEqual.equalTo;

public class Xml_demo {
    @Test
    public void  testXML(){
        given().log().all()
                .get("http://localhost:8329/demo.xml").prettyPeek()
                .then()
                //断言category的数量为3
                .body("shopping.category.size()",equalTo(3))
                //断言category中type为supplies的数量为1
                .body("shopping.category.find{it.@type=='supplies'}.size()",equalTo(1))
                //断言category中type为supplies的第一个item中的price为5
                .body("shopping.category.find{it.@type=='supplies'}.item[0].price",equalTo("5"));

    }
}

 

使用proxy进行调试  请求时发现异常,走代理进行调试

package restassured;

import org.junit.Test;

import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.core.IsEqual.equalTo;

public class Xml_demo {
    @Test
    public void  testXML(){
        given().log().all()
                //配置代理
                .proxy("10.231.21.240",8888)
                .get("http://localhost:8329/demo.xml").prettyPeek()
                .then()
                //断言category的数量为3
                .body("shopping.category.size()",equalTo(3))
                //断言category中type为supplies的数量为1
                .body("shopping.category.find{it.@type=='supplies'}.size()",equalTo(1))
                //断言category中type为supplies的第一个item中的price为5
                .body("shopping.category.find{it.@type=='supplies'}.item[0].price",equalTo("5"));

    }
}

Charles中可成功抓取到请求数据

 

断言体系

❖  断言体系

    • assert体系:从response中取数据进行单独的assertEuqal断言
    • Hamcrest体系:assertThat(x, is(3))
  • RestAssured断言
    • body断言
      • XmlPath
      • JsonPath
      • HtmlPath
    • 结合Hamcrest断言matcher

 

导出response中的值

.extract().response()
package restassured;

import io.restassured.response.Response;
import org.junit.BeforeClass;
import org.junit.Test;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.useRelaxedHTTPSValidation;
import static org.hamcrest.Matchers.hasItems;
import static org.junit.Assert.assertEquals;

public class Json_Testerhome {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }

    @Test
    public void topic(){
        Response response = given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json").prettyPeek()
                .then()
                //获取第一个topic的id
//                .body("topics.id[0]",equalTo(18739))
                //获取最后一个topic的user的login信息
//                .body("topics[-1].user.login",equalTo("cmlanche-github"))
                //获取id为18316的topic的title
//                .body("topics.find{it.id==18316}.title",
//                equalTo("[深圳] 微众银行内部人才招聘高级测试工程师 / 测试工程师"))   //页面修改
                //hasItems实例
                .body("topics.id",hasItems(18316,18739))
                .extract().response();
        String id1=response.path("topics.id[0]");
        String id2=response.path("topics.id[1]");
         assertEquals(id1,id2);
    }
}

 

 

 


body断言

  • GPath断言:支持xmljson
    • shopping.category.item[0].name
    • store.book[-1].category
    • shopping.category.item.size()
    • topics.find { it.id == '19191' }.user.name
  • xml特有断
    • **.find { it.name == 'Chocolate' }.price

**.find { it.name == 'Chocolate' }.price

package restassured;

import org.junit.Test;

import static com.jayway.restassured.RestAssured.given;
import static org.hamcrest.core.IsEqual.equalTo;

public class Xml_demo {
    @Test
    public void  testXML(){
        given().log().all()
                //配置代理
                .proxy("10.231.21.240",8888)
                .get("http://localhost:8329/demo.xml").prettyPeek()
                .then()
                //断言category的数量为3
                .body("shopping.category.size()",equalTo(3))
                //断言category中type为supplies的数量为1
                .body("shopping.category.find{it.@type=='supplies'}.size()",equalTo(1))
                //断言category中type为supplies的第一个item中的price为5
                .body("**.find{it.@type=='supplies'}.item[0].price",equalTo("5"));

    }
}

 

 

 

课后作业

  • testerhome 页验证
    • https://testerhome.com/api/v3/topics.json
    • 断言精华帖的数量大于4

 

package restassured;

import org.junit.BeforeClass;
import org.junit.Test;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.useRelaxedHTTPSValidation;
import static org.hamcrest.Matchers.greaterThan;

public class Json_Testerhome_hw {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }

    @Test
    public void topic(){
         given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json")
                .then()
                .body("topics.findAll{it.excellent==1}.size()",greaterThan(4));
    }
}

 

Groovy语法中的findAll 与 find

接口测试用例数据驱动&高级断言_rest_assured_第4张图片

https://www.w3cschool.cn/groovy/groovy_closures.html

 

业务测试:

方法一:

将登陆写在@BeforeClass中 

后面的方法直接调用登陆信息即可

 

方法二:

多个接口互相调用

封装成API直接调用

多次调用需要去被抽象成标准的函数,在方法内被调用即可

 

 

package restassured;

import io.restassured.response.Response;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.concurrent.TimeUnit;

import static io.restassured.RestAssured.given;
import static io.restassured.RestAssured.useRelaxedHTTPSValidation;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;

public class Json_Testerhome {

    @BeforeClass
    public static void BeforeClass(){
        useRelaxedHTTPSValidation();
    }


    public void topic(){
        Response response = given()
                .when()
                .get("https://testerhome.com/api/v3/topics.json").prettyPeek()
                .then()
                //获取第一个topic的id
//                .body("topics.id[0]",equalTo(18739))
                //获取最后一个topic的user的login信息
//                .body("topics[-1].user.login",equalTo("cmlanche-github"))
                //获取id为18316的topic的title
//                .body("topics.find{it.id==18316}.title",
//                equalTo("[深圳] 微众银行内部人才招聘高级测试工程师 / 测试工程师"))   //页面修改
                //hasItems实例
                .body("topics.id",hasItems(18316,18739))
                .extract().response();
        String id1=response.path("topics.id[0]");
        String id2=response.path("topics.id[1]");
         assertEquals(id1,id2);
    }
    @Test
    public void baidu() {
        topic();  //此处直接调用即可
        given()
                .log().all()
                .queryParam("wd", "mp3")
                .when()
                .get("http://www.baidu.com/s")
                .then()
                .log().all()
                .statusCode(200)
                .body("html.head.title", equalTo("mp3_百度搜索"))
                .time(lessThan(2L), TimeUnit.SECONDS);
    }
}

 

 

Json schema

 

 

 

 

 

 


 

你可能感兴趣的:(测试开发,霍克沃兹,接口测试)