接口测试与技术选型

自动化测试技术11:接口测试技术选型与TestNG接口测试实践

一、服务端接口测试介绍

什么是服务端?

一般所说的服务端是指为用户在 APP 或 PC 使用的互联网功能提供数据服务的背后的一切。以天猫精灵智能音箱系列的产品链路为例,服务端便是网关(包括网关在内)之后的链路。

图片

                   

3、什么是接口测试?

一般讲的接口测试指的是对某个给定接口进行功能测试,输入不同的参数时,接口返回值是否正确。下图是经典的测试金字塔模型。

图片

在这个模型中,越往下比例会占的越高,也就是说在一个产品测试中,单元测试比例是最高的,依次是接口测试和UI自动化测试,最顶端是人工测试部分。服务端接口测试在中部,承上启下,由此可见其重要性。

3、接口测试的特点?

一般做接口测试有如下原因:

  • 针对服务端后台测试,接口规则一旦确定,后期的变化非常的小

  • 接口是服务端对外提供数据服务最常用的信息交换方式,接口大部分内容都是数据,通过数据对比我们可以推测到系统的逻辑,测接口其实也就是测逻辑。

  • 接口测试相对容易实现自动化,也容易实现持续集成,相对于变化频繁的UI来说,接口测试的性价比更高,可以减少人工回归测试人力成本与时间,缩短测试周期,支持后端快速发版需求。

下面是一张关于接口测试范围地图:

图片

二、接口测试涉及技术和工具简介

1、Java 代码接口测试

为什么要用代码做接口自动化测试呢?一些工具功能是有限制,很多公司需要一些特定的功能,工具不支持,只好用代码进行开发。一般用 Java 做自动化测试,主要利用 httpclient.jar 包,然后利用 JUnit 或者 TestNG 这样的单元测试工具,进行测试用例的开发,接着在 Jenkins 或我们的 aone 上创建一个 job,进行持续集成测试。

图片

2、Python 代码接口测试

和 Java 一样,用 Python 做接口测试,可以利用一个功能强大的第三方库 Requests,它能方便地创建接口自动化用例。Python 下的单元测试框架,一般采用 unittest。生成测试报告,一般选择 HTMLTestRunner.py。同样,可以结合 Jenkins 做持续集成测试。

图片

3、Fiddler

Fiddler 是一个 HTTP 协议调试代理工具,Web 和手机测试都会用到,同时也支持接口测试。它能够记录并检查所有你的电脑和互联网之间的 http 通讯,设置断点,查看所有的“进出”Fiddler 的数据(指 cookie,html,js,css 等文件)。

图片

4、Postman

它是 Google 开发的一个插件,安装在 Chrome 浏览器上,能支持不同接口测试请求,可以管理测试套件和自动化运行。弱点是自动化断言功能不强大,不能和 Jenkins、代码管理库进行持续集成测试。

5、Wireshark

这是一款抓包工具,支持 TCP、UDP、HTTP 等协议。如果做底层网络数据测试,一般都需要用到它,但是用作接口测试,它就有点不友好。因为刷新数据太快,不好定位每个操作对应的接口。

6、SoupUI

soapUI 是一个开源测试工具,通过 soap/http 来检查、调用、实现 Web Service 的功能/负载/符合性测试。该工具既可作为一个单独的测试软件使用,也可利用插件集成到 Eclipse,maven2.X,Netbeans 和 intellij 中使用。把一个或多个测试套件(TestSuite)组织成项目,每个测试套件包含一个或多个测试用例(TestCase),每个测试用例包含一个或多个测试步骤,包括发送请求、接受响应、分析结果、改变测试执行流程等。该工具能够支持接口自动化测试和接口性能测试,也支持和 Jenkins 做持续集成测试。

三、TestNG结合HttpClient接口测试实践


1、HttpClient基本用法

HttpClient实现了基于HTTP的各种方法,Get,Post,Put等等,支持HTTPs,关于如何支持,可以参考我之前的文章 Apache HttpClient 4.5实现https_编故事的逗老师的专栏-CSDN博客

HttpClient基本的用法也很简单,一看就会,主要是看你怎么构建代码,以post请求为例:

1. 初始化httpclient

CloseableHttpClient httpClient = HttpClientBuilder.create().build()

2. 创建请求

HttpPost httpPost = new HttpPost("url");

3. 设置header(可选)

httpPost.setHeader("Content-Type", "application/json;charset=utf8");

4. 设置参数(可选)

Parameters parms = new User();

parms .setXxx("xxx");

// required fastjson
String jsonString = JSON.toJSONString(user);

StringEntity entity = new StringEntity(jsonString, "UTF-8");

httpPost.setEntity(entity);

5. 执行请求

CloseableHttpResponse response = httpClient.execute(httpPost);

6. 获取相应

HttpEntity responseEntity = response.getEntity();

将方法封装,将参数对象化,再使用Junit,TestNG等工具搭建框架,则一个基于HttpClient的接口测试框架就能够实现了。

2、HttpClient与TestNG结合

1)不携带cookie的get请求

 

package com.course.httpclient.Demo;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.testng.annotations.Test;

import java.io.IOException;

public class MyHttpClient {

@Test
public void test1() throws IOException{

//用来存放测试结果
String result;
//创建一个get实例,并指定请求url
HttpGet get = new HttpGet("http://www.baidu.com");
//创建一个client对象,是用来执行get方法的
CloseableHttpClient client = HttpClients.createDefault();
//用客户端执行get实例,并把响应结果保存在response对象中
HttpResponse response =client.execute(get);
//response.getEntity()用来获取整个响应的实例,即获取整个响应内容
//EntityUtils对象是org.apache.http.util下的一个工具类,用官方的解释是为HttpEntity对象提供的静态帮助类
//把响应内容转成字符串,转换时使用编码为utf-8
result = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println(result);

}
}

2)携带cookie的get请求

 

package com.course.httpclient.Cookies;

import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;

public class MyCookiesForGet {

private String url;
private ResourceBundle bundle;
private HttpClientContext context;
private CloseableHttpClient client;

@BeforeTest
public void beforeTest(){
/* ResourceBundle工具类,默认获取properties配置文件,因而这里只需写配置文件名即可,不需写后缀
这里获取配置文件application,因为是在resource下,所以直接写配置文件名即可,不需加路径,
若配置文件不在resource下,则此处需要写上相对于resource的相对路径,注:这里需要加字符编码,如中文编码Locale.CHINA*/
//成员属性,赋值时可以拿来直接赋值,该赋值是全局性的
bundle = ResourceBundle.getBundle("application", Locale.CHINA);
//通过工具类里的getString方法,获取配置文件里相应的键对应的值,这里获取test.url的值,获取成功时,相应配置文件里的键会变颜色,如黄色
//这里给本类的成员属性赋值
url = bundle.getString("test.url");
}

@Test
public void testGetCookies() throws IOException {

//用来存放测试结果
String result;
//从配置文件中拼接测试的url;this,表示当前对象,this.url表示引用本类的成员属性url,引用成功时,属性颜色会变化
String uri = bundle.getString("getCookies.uri");
String testUrl = this.url+uri;
//创建一个get实例,并指定请求url
HttpGet get = new HttpGet(testUrl);

// 全局请求设置,用于获取cookie并携带cookie
RequestConfig globalConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
//实例化一个cookie存储对象
CookieStore cookieStore = new BasicCookieStore();
// 创建HttpClient上下文
context = HttpClientContext.create();
context.setCookieStore(cookieStore);

//创建一个client对象(客户端),是用来执行get方法的,获取cookie并把cookie存放在全局请求设置中,如有的话
//静态方法,即类方法,可以不创建对象,直接引用
client = HttpClients.custom().setDefaultRequestConfig(globalConfig).setDefaultCookieStore(cookieStore).build();
//用客户端执行get实例,并把响应结果保存在response对象中,同时把cookie等信息存放在context上下文中
HttpResponse response =client.execute(get,context);
//response.getEntity()用来获取整个响应的实例,即获取整个响应内容
//EntityUtils对象是org.apache.http.util下的一个工具类,用官方的解释是为HttpEntity对象提供的静态帮助类
//把响应内容转成字符串,转换时使用编码为utf-8
result = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println(result);

//获取获取cookieStore对象中的cookies信息,并保存在对象列表中
//List<类名> 对象名:申明一个对象列表,用来存储多个对象,其中的数据存储在相应对象的方法中
List<Cookie> cookies = cookieStore.getCookies();

//将cookie信息遍历出来,并保存在cookie对象中,即将对象列表中的对象遍历出来
for (Cookie cookie : cookies){
String name =cookie.getName(); //获取cookie名,通过遍历出来对象,调用相应的方法获取对象里的数据
String value = cookie.getValue(); //获取cookie值
System.out.println("cookie name = " + name + " ;cookie value = " + value);
}
}

@Test(dependsOnMethods = {"testGetCookies"})
public void testGetWithCookies() throws IOException {

String result;
String uri = bundle.getString("getdemo.withCookies");
String testUrl = this.url+uri;
//创建一个get实例,并指定请求url
HttpGet get = new HttpGet(testUrl);
//执行get请求,并带上上下文的全局设置中的cookie信息
HttpResponse response =this.client.execute(get,this.context);

//获取响应状态码
int statusCode = response.getStatusLine().getStatusCode();
System.out.println("statusCode = " + statusCode);

if (statusCode == 200){
//输出响应结果
result = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println(result);
}
}
}

  • 成员属性,在本类内使用,可以不加this,是全局属性;方法内定义的属性是局部的,在方法内使用;其实在方法体中引用成员变量或其他的成员方法时,引用前都隐含着“this.”,一般情况下都会缺省它,但当成员变量与方法中的局部变量同名时,为了区分且正确引用,成员变量前必须加“this.”不能缺省。

  • 在resources下的配置文件application.properties,直接给src中的类包提供需要的配置值,内容如下

 

application.properties


test.url=http://localhost:8866
dev.url=http://localhost:8866

getCookies.uri=/getCookies
getdemo.withCookies=/getdemo/withCookies
postdemo.withCookies=/postdemo/withCookies
login=/login

3、TestNG数据驱动测试实现

在实际的测试工作中,我们经常会遇到如此场景:同样的测试操作步骤,但有多组不同的测试输入数据,需要执行多次测试操作。如果每条数据都去编写一个测试用例,这样难免增加代码量和维护量。在自动化测试中,针对这种情况,我们可以通过TestNG中的@DataProvider注解来轻松实现这种数据驱动操作,我们也可以把数据驱动理解为是测试数据和测试行为分离。

数据源和使用在同一个类中

在同一个类中通过@DataProvider注解的方法提供数据源。同时在需要用到数据源的@Test中添加“dataProvider ”参数表明测试数据的来源。

代码示例:

 

import org.testng.annotations.DataProvider;import org.testng.annotations.Test;
/** * Created by 米阳 on 6/10/2017. */public class DataProviderTest {
//在@DataProvider注解中为数据源命名 testData@DataProvider(name = "testData")public Object[][] data() {return new Object[][]{ {"name", "pwd"}, {"test1", "test2"}, {"test3", "test4"} }; }
//在@Test注解中指定dataProvider参数并设置获取数据的来源 testData@Test(dataProvider = "testData")public void dataDriverTest(String name, String passwork) { System.out.println("使用账号:"+name+"_和密码:"+passwork+" 登录!"); }}

从执行结果中我们可以看到虽然整个类我们就一个@Test但是却运行了三次,每次的参数都不一样均来自数据源的一组数据。

数据源在单独类中

我们除了可以把数据源和测试用例放一个类中,也可以把数据源单独抽取到一个类中进行管理。
这样我们@Test在使用数据源时除了要指定 dataProvider 参数外,还需要指定 dataProviderClass 参数,例如dataProviderClass=包名.类名.class

代码示例:

数据类Data:

 

import org.testng.annotations.DataProvider;
/** * Created by 米阳 on 6/10/2017. */public class Data {@DataProvider(name = "dataTest")public static Object[][] dataDriver() {return new Object[][]{ {1}, {2}, {3} }; }}

其它类中使用这个数据源:

 

//获取 com.seleium.datadriver包下的Data类中的名为 dataTest的数据源 @Test(dataProvider = "dataTest", dataProviderClass = com.selenium.datadriver.Data.class)public void dataDriverTest2(int a) { System.out.println(a); }

说明:

  1. @DataProvider 的name参数为非必须,如果没有name参数,那么可以理解默认name值为就是对应方法名。

  2. 数据源的数据为什么类型的数据,则在使用该数据源时的@Test方法的参数就得为该类型的参数并且参数个数要等于数据源每组的数据个数。

  3. 被@DataProvider 注释的方法即数据源,该方法必须返回一个Object[][] 对象。

4、并发测试实现

当我们有成千上万个自动化测试用例时,为了提高测试用例的执行速度,往往我们需要对测试用例进行并发执行。并发执行的方式很多,而较为简单的方式就是通过TestNG提供的并发来完成。

DataProvider 的并发

上节我们已经知道了如果通过@DataProvider进行数据驱动,那当我们有多个数据源时我们想一次性执行多个数据源该如何设置呢?
我们只需要在@DataProvider 标签中添加 parallel参数并设置值为true便可。

代码例子:

 

@DataProvider(name = "data1",parallel = true)public Object[][] data2() {return new String[][]{ {"name", "gsfdg", "fadsf"},{"a","v","c"} }; }
//在@Test注解中指定dataProvider参数并设置获取数据的来源 testData@Test(dataProvider = "data1")public void dataDriverTest(String name, String i, String b) throws InterruptedException { System.out.println(name + i + b); Thread.sleep(5000); }

代码里面我们在data2()方法中添加了@DataProvider注解,并添加了parallel=true参数,那么所有用到该数据源的测试方法都将并发执行,所以如下图当这个脚本运行起来时,我们会发现IDEA 同时执行多个测试。但是当我们的数据源操作10条时,默认同时执行10条数据,也就是DataProvider的默认线程为10。

设定DataProvider 的并发数

当我们知道初步了解了TestNG的执行xml后,我们只需要简单修改下<suite>标签,在里面添加“data-provider-thread-count”属性并设置值,那么我们就可以设定DataProvider的并发数。

注意通过IDEA执行时,不可以在测试方法上右键 -->run ,而是需要在执行的xml文件上右键 -->run

test,class , method 级别的并发

TestNG除了支持DataProvider的并发外,也支持test, class , method 级别的并发。设置基本一样都是在<suite> 标签添加parallel 和 thread-count 参数,其中parallel参数表示采用哪种级别的并发,thread-count参数表示并发的线程数,如下示例:

 

<suite name="Default Suite" parallel="tests" thread-count="10"><suite name="Default Suite" parallel="classes" thread-count="10"><suite name="Default Suite" parallel="methods" thread-count="10">

说明:

tests 级别:我们在执行的xml中配置多个<test>标签,则每个<test>标签内的测试用例执行在同一个线程中。
classes 级别:我们在执行的xml中配置多个<class>标签,则每个<class>标签内的测试运行在同一个线程中。
methods 级别:则运行所有的执行xml中配置的测试用例都于不同的线程中。

四、用Fiddler工具进行流量抓包


Fiddler简介

  • 可以抓到请求数据,查看Raw格式/表单格式/Json/XML格式

  • 可以拦截和修改请求

  • 更强大的过滤器

  • 可以抓取Postman/接口脚本发送的请求,方便调试

  • 可以抓包手机请求 ...

Fiddler主界面

Fiddler的主界面分为 工具面板、会话面板、监控面板、状态面板

图片

  • Inspectors: 检查员

    • Raw:请求的原始格式

    • WebForm: 请求的表单格式

    • Json:请求的Json格式请求

    • XML:请求的XML格式

  • AutoResponsder: 自动回复,可用于构造响应,Mock,不修改服务器文件调试接口

  • Composer: 设计者, 发送和调试请求

  • FidderScript:

  • Filters: 过滤器

    • Hosts: 按服务器过滤

    • Clients Process: 按客户端程序过滤

    • Request Headers: 按请求头过滤

    • Breakpoints: 设置断点

    • Response Status Code: 按状态码过滤

    • Response Type and Size: 按响应类型及大小过滤

    • Response Headers: 按响应头过滤

自动断点设置
菜单Rules -> Automatic Breakpoints -> Before Requests/After Requests

手机抓包
安装fiddler的笔记本和手机使用同一wifi -> 手机长按该wifi,选择高级 -> 添加代理 ip为笔记本ip, 端口为8888 -> 笔记本开启fiddler, 手机端访问网页

参考:https://www.cnblogs.com/conquerorren/p/8472285.html#

五、深入接口测试之Mock技术


1、Mock工具的选择

Mock测试工具分为单元测试级别的Mock工具和接口测试级别的Mock工具。单元测试级别的Mock工具有easymock、jMock、Mockito、Unitils Mock、PowerMock、JMockit等等。就目前来讲,是mockit+PowerMock、JMockit这两种工具使用人数较多。

以下是接口测试级别的一些Mock工具,接口级别的Mock工具完成的主要功能是对一个用户的请求,模拟server返回一个接口的响应数据。

(1)Wiremock

github地址:https://github.com/tomakehurst/wiremock
特点:
- 支持Http响应头,匹配URL,heade和body内容模式
- 请求验证
- 可以作为一个独立的进程或者WAR app在单元测试中运行
- 可以通过Java API,Json文件和JSON over HTTP配置
- 有记录/回放功能
- Fault injection
- 可以作为请求检查和替换的浏览器代理
- 有状态的行为模拟
- 可配置响应延迟
(2)Mockserver
github地址:https://github.com/jamesdbloom/mockserver
特点:
- 能够mock HTTP或者HTTPS 的server或者服务
- 当一个请求匹配 expectation时能够返回一个mock response
- 当一个请求匹配 expectation时能够forward 一个请求
- 当一个请求匹配 expectation时能够执行一个回调(callback),允许动态地创建response
- 支持Request验证
(3)Moco
github地址:https://github.com/dreamhead/moco
特点:
- an easy setup stub framework.
- 支持HTTP、HTTPS、SOCKS
- 支持单元测试
(4)Mock.js
github地址:https://github.com/nuysoft/Mock/tree/refactoring
特点:
- 根据数据模板生成整合后的数据
- 对于Ajax请求提供request/response mocking
(5)RAP
github地址:https://github.com/thx/RAP
特点:
- Web接口管理工具,接口自动化,MOCK数据自动生成,自动化测试
- 能够通过分析接口结构自动生成Mock数据、校验真实接口的正确性
- 阿里产品,功能完善、结合了文档、Mock.js、可视化、Rest、接口过渡、文档修改提醒、支持本地部署。

2、使用WireMock环境搭建Mock平台

这里使用wiremock环境搭建Mock平台,具体方法如下:

WireMock 的独立安装版本其实就是一个Jar包,可以从Maven仓库中下载或者也可以在Java Maven项目中通过 pom.xml的依赖添加:

<dependency><groupId>com.github.tomakehurst</groupId><artifactId>wiremock-jre8-standalone</artifactId><version>2.26.3</version><scope>test</scope></dependency>

除了Standalone 版本, 也可以直接在 Java代码中引用,对应Maven依赖:

<dependency><groupId>com.github.tomakehurst</groupId><artifactId>wiremock-jre8</artifactId><version>2.26.3</version><scope>test</scope></dependency>

WireMock 命令行用法

WireMock 的独立版本是一个 Jar 包,所以运行 Wiremock 自然是需要 Java 基础环境,命令如下:

java -jar wiremock-standalone-2.26.3.jar

命令行支持的一些主要参数及其作用说明如下:

  • --port 设置 Mock 服务的 http 端口,默认启动在 8080 端口

  • 如果设置为0,则自动确定端口

  • --https-port 设置 https 的端口

  • --verbose 在终端显示详细的日志信息

  • --root-dir 设置 mappings 和 __files 目录的工作路径

  • --enable-browser-proxying 以浏览器代理的方式运行

单来说,WireMoc 会在本地启动一个侦听指定端口的web服务,这里指定的端口可以用 --port 指定http协议 或 --httpsport指定https协议端口。启动后我们发到指定端口的请求,就会由WireMock来完成响应,达到接口Mock的目的。

启动后,我们在本地运行目录下会看到 WireMock会自动生成__files 和 mappings两个目录。这两个目录中存放的就是Mock模拟的接口匹配内容了。

  • __files 存放接口响应中会用到的一些文件资源

  • mappings 存放接口响应匹配规则

具体的匹配方法我们后面再介绍,这里先介绍下WireMock本身自带的Admin接口。

Wiremock Admin 接口

WireMock 本身支持管理接口,启动后根据启动的端口,访问http://localhost:8080/__admin/docs/ ,可以看到如下两个链接:

 
  1. OpenAPI 3.0 spec

  2. Swagger UI

可以用 Swagger UI接口定义格式查看支持的管理接口

 

http://localhost:8080/__admin/swagger-ui/

WireMock基本用法

1)在mappings下创建一个*.json文件

{"request": {"method": "GET","url": "/api/mocktest"},"response": {"status": 200,"bodyFileName": "response.json","headers": {"Content-Type": "application/json","Cache-Control": "max-age=86400"}}}

bodyFileName还可以是html、xml等类型的文档。

2)在__files下创建响应文件

上例中response.json 就是需要我们在__files里面建立的响应文件。其内容为:

{"test":"wiremock"}

在浏览器或者使用curl命令,调用

http://localhost:8080/api/mocktest ,就能返回test.json的内容了。

当然,你也可以不用创建这个文件,直接在request中将

bodyFileName改成直接body写出这个json也可以:

{"request": {"method": "GET","url": "/api/mocktest"},"response": {"status": 200,"body": "{\"test\":\"wiremock\"}","headers": {"Content-Type": "application/json","Cache-Control": "max-age=86400"}}}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值