在自动化测试的知识体系中,最推荐使用API的自动化测试,在基于API的自动化测试中,也是提升测试效率最有效的

手段之一。这是因为,在UI的自动化测试中,它是无法满足互联网公司的快速交付的节奏,UI自动化测试不管是否承认于是

否,它在执行上面都是存在缺陷的,也就是它的执行耗时是很长的,虽然可以引入显式等待以及隐式等待的技术,但是还是

无法来解决这个问题。在谈到API的自动化测试知识体系中,我们需要掌握HTTP的协议,也就是应用层的协议,需要系统性

的了解常用的请求方法,请求状态码,请求头以及响应头信息,和服务之间的通信模式。在API的自动化测试没模式中,其

实就是请求/响应的模式,为什么这样说了?我们先不回答这个问题,我们就拿目前比较主流的微服务架构而言,它是基于

轻量级的REST API的通信模式来进行交互,那么在服务进行交互的时候,通常有同步通信和异步通信,不管是同步模式还

是异步模式,它都是基于应用层的协议,所以我们首先需要清楚的知识一个完整的HTTP的请求流程,具体如下:

 

 

在如上的模式中,我们可以看到,在客户端与服务端之间的交互中,涉及到的主要步骤为:

  • HTTP是基于应用层的协议,它不需要关注底层网络传属层协议的事,但是客户端与服务交互首先需要建立TCP的连接
  • 建立TCP的连接后,客户端向服务端发送Request的请求,当然这中间可能涉及到同步通信,也可能是异步通信
  • 服务端接收到客户端的请求信息后,服务端响应回复客户端,也就是Response的过程。
  • 最后,客户端与服务端之间的交互完成后,客户端与服务端之间关闭TCP的连接,如不关闭,就会导致性能上的损失

如上我们一直说到服务之间的交互,首先我们来谈同步交互,同步通信就是客户端向服务端发送请求后,服务端必须回应客户端

的的请求,所以它的缺陷也是非常明显的,也就是说服务端可能网络延迟的情况下,无法回应客户端,但是客户端在等待服务端

的回应,也就存在了超时,这也是同步通信中最大的缺陷。如下显示的是同步通信中的交互模式,如下所示:

 

 

由于同步通信的超时缺陷,以及在某些业务形态中,其实客户端发送请求后,并不需要服务端立刻马上有所回应的,比如下载

文件的业务,客户端发送请求后,下载文件需要一个过程,那么这个过程中,并不需要刻意的等待,客户端可以干其他的事,

这时候就可以使用到异步通信的模式,也就是说客户端与服务端并不需要知道对方的存在,更多主要关注的是MQ的机制,也

就使用到消息中间件,主流的消息中间件主要为RabbitMQ和Kafka(后期文章中针对这两个消息中间件会做系统权威的案例

解读)。我们还是通过交互图来看异步通信的模式,如下所示:

 

 

在如上中,可以看到客户端和服务端之间使用的是轻量级的MQ的中间件来进行交互。其实我们抛开客户端与服务端的角色,

其实在微服务架构这样的模式中,整个客户端发送请求后,它首先会经过API GateWay后,再发送给服务端,当然这个过程

它的链路是比较长的,具体交互如下:

 

 

      当然在今天的文章体系中,我们主要讲的并不是这些理论的知识。但是有一点我们需要认可,也就是说理论指导实践,

而实践再来推导理论,这是一个完整的方法论体系。在具备如上的理论体系中,我们今天主要学习REST-Assured框架来

实现API的自动化测试,我们首先需要在maven中引入它,也就是pom.xml文件,在pom.xml文件中引入REST-Assured,

pom.xml文件的内容为:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>ngApp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <!--编译编码-->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <aspectj.version>1.8.10</aspectj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>6.10</version>
            <scope>compile</scope>
        </dependency>

        <!--WebDriver的测试框架-->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-server</artifactId>
            <version>3.141.59</version>
        </dependency>

        <!--引入Allure的框架-->
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-testng</artifactId>
            <version>2.0-BETA14</version>
            <scope>test</scope>
        </dependency>

        <!--log4的模块-->
        <!-- https://mvnrepository.com/artifact/log4j/log4j -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <!--fastjson库-->
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <!--单元测试覆盖率信息-->
        <!-- https://mvnrepository.com/artifact/org.jacoco/jacoco-maven-plugin -->
        <dependency>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.8.6</version>
            <scope>test</scope>
        </dependency>

        <!--mockito框架-->
        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-all -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
        </dependency>

        <!--REST Assured框架-->
        <!-- https://mvnrepository.com/artifact/io.rest-assured/rest-assured -->
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>4.3.3</version>
            <scope>test</scope>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>

            <!--Alure测试报告插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>

                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                    <!--生成allure-result的目录-->
                    <systemProperties>
                        <property>
                            <name>allure.results.directory</name>
                            <value>./target/allure-results</value>
                        </property>
                    </systemProperties>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>

            <!--检查代码覆盖率的插件配置-->
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.6</version>
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                        <configuration>
                            <!--指定jacoco.exec的目录-->
                            <destFile>target/jacoco.exec</destFile>
                            <propertyName>jacocoArgLine</propertyName>
                        </configuration>
                    </execution>
                    <execution>
                        <id>check</id>
                        <goals>
                            <goal>check</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>

                <!-- Configuration 里面写配置信息 -->
                <configuration>
                    <!-- rules里面指定覆盖规则 -->
                    <rules>
                        <rule implementation="org.jacoco.maven.RuleConfiguration">
                            <element>BUNDLE</element>
                            <limits>
                                <!-- 指定方法覆盖到80% -->
                                <limit implementation="org.jacoco.report.check.Limit">
                                    <counter>METHOD</counter>
                                    <value>COVEREDRATIO</value>
                                    <minimum>0.80</minimum>
                                </limit>
                                <!-- 指定指令覆盖到80% -->
                                <limit implementation="org.jacoco.report.check.Limit">
                                    <counter>INSTRUCTION</counter>
                                    <value>COVEREDRATIO</value>
                                    <minimum>0.80</minimum>
                                </limit>
                                <!-- 指定行覆盖到80% -->
                                <limit implementation="org.jacoco.report.check.Limit">
                                    <counter>LINE</counter>
                                    <value>COVEREDRATIO</value>
                                    <minimum>0.80</minimum>
                                </limit>
                                <!-- 指定类覆盖到100%,不能遗失任何类 -->
                                <limit implementation="org.jacoco.report.check.Limit">
                                    <counter>CLASS</counter>
                                    <value>MISSEDCOUNT</value>
                                    <maximum>0</maximum>
                                </limit>
                            </limits>
                        </rule>
                    </rules>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.14.1</version>
                <configuration>
                    <!--testng配置文件名称-->
                    <suiteXmlFiles>
                        <suiteXmlFile>testng.xml</suiteXmlFile>
                    </suiteXmlFiles>
                    <threadCount>1</threadCount>
                    <parallel>tests</parallel>
                    <!--测试失败后忽略继续执行-->
                    <testFailureIgnore>true</testFailureIgnore>
                    <skipTests>false</skipTests>
                    <argLine>-Xmx256M ${jacocoArgLine}</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

 

下面的源码是被测试服务的源码信息,该源码主要是使用Flask进行编写的,具体源码信息为:

#!/usr/bin/env python
#!coding:utf-8


from flask import  Flask,jsonify
from flask_restful import  Api,Resource,reqparse


app=Flask(__name__)
api=Api(app)


class LoginView(Resource):
    def get(self):
        return {\'status\':0,\'msg\':\'ok\',\'data\':\'this is a login page\'}

    def post(self):
        parser=reqparse.RequestParser()
        parser.add_argument(\'username\', type=str, required=True, help=\'用户名不能为空\')
        parser.add_argument(\'password\',type=str,required=True,help=\'账户密码不能为空\')
        parser.add_argument(\'age\',type=int,help=\'年龄必须为正正数\')
        parser.add_argument(\'sex\',type=str,help=\'性别只能是男或者女\',choices=[\'\',\'\'])
        args=parser.parse_args()
        return jsonify(args)

api.add_resource(LoginView,\'/login\',endpoint=\'login\')

if __name__ == \'__main__\':
    app.run(debug=True)

 

我们运行该服务后,使用Rest Assured的框架来发送一个GET的请求,具体源码如下:

package test.api;

import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import io.restassured.http.Method;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

public class RestApi
{
    @BeforeMethod
    public void setUp()
    {
        RestAssured.baseURI="http://127.0.0.1:5000";
    }

    @Test(description = "对登录服务发送GET的请求")
    public void test_login_get()
    {
        RequestSpecification httpRequest=RestAssured.given();
        Response r=httpRequest.request(Method.GET,"/login");
        String  responseBody=r.getBody().asString();
        System.out.println("响应结果信息:"+responseBody);
    }
}

 

我们首先来解读如上的代码信息,在初始化的方法里面,我们首先填写我们的请求地址,下来然后是获取需要发送的服务端的地址,

然后进行发送GET的请求,最后是得到服务端Response回复给客户端的结果信息,如上的代码执行后,它的结果信息为:

响应结果信息:{
    "status": 0,
    "msg": "ok",
    "data": "this is a login page"
}

如上就是我们发送的GET的请求,下来我们再来看Rest Assured验证结果信息,主要包含了协议状态码,业务状态码以及服务端

返回的其他字段的信息验证。那么我们就需要加入断言的结果信息,具体代码如下:

        Assert.assertEquals(r.statusCode(),200);

我们知道发送请求后,服务端返回给我们的是基于JSON格式的字符串,那么我们首先需要获取到JSON的文件,然后针对JSON的文件

里面的具体key-value的字段信息进行验证,如data怎么确定它返回的结果信息是“this is a login page”,所以意味着我们需要获取JSON

文件并且进行处理,我们可以使用字符串包含的方式来,具体案例代码为:

Assert.assertTrue(responseBody.contains("this is a login page"));

 

虽然这种方式也是可以的,但是还是没有达到我们的基本要求,也就是验证key-value的方式,对响应结果信息JSON方式的处理,所

以如下具体演示这种方式,案例代码为:

        Assert.assertEquals(r.jsonPath().get("data"),"this is a login page");

这样我们就能够获取key-value具体的值了,如获取msg的信息,涉及到的源码为:

        String msg=r.jsonPath().get("msg");
        System.out.println("msg output:"+msg);

 

所以如上完整的断言结果信息代码为:

package test.api;

import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import io.restassured.RestAssured;
import io.restassured.http.Method;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

public class RestApi
{
    @BeforeMethod
    public void setUp()
    {
        RestAssured.baseURI="http://127.0.0.1:5000";
    }

    @Test(description = "对登录服务发送GET的请求")
    public void test_login_get()
    {
        RequestSpecification httpRequest=RestAssured.given();
        Response r=httpRequest.request(Method.GET,"/login");
        String  responseBody=r.getBody().asString();
        Assert.assertEquals(r.statusCode(),200);
        Assert.assertTrue(responseBody.contains("this is a login page"));
        Assert.assertEquals(r.jsonPath().get("data"),"this is a login page");
    }
}

 

这样我们就完成了一个发送请求后,针对返回的数据进行断言验证的信息。

     感谢您的阅读,后续会持续更新!

 

版权声明:本文为weke原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/weke/articles/14327985.html