这是一个Restful API自动化测试框架,这是一个能让你写出高可读性测试代码的测试框架!

项目目标##

话说目前行业内,Restful API自动化测试框架已经不是稀罕物了,各个语言都有自己的实现机制。拿Java的Jersey来讲,它本身就提供了一个API测试框架-Jersey Test Framework.能够帮助我们写API测试,但是这里我们想做的是另一套。

观察到Jersey使用了Fluent interface的模式来让代码可读性更高,比如下面:

String responseMsg = target.path("myresource").request().get(String.class);

那么如果我们也使用Fluent Interface模式,是不是也可以让我们的测试代码可读性更高呢?

比如下面的测试的代码,是不是看起来很清爽,目标更明确呢?

APIRequest.GET(URL).header("Authorization", "Bearer " + token).invoke().assertStatus(200).assertBody(expectedBody);

直接一行代码,搞定一条Case!

分析需求##

既然是一个API自动化测试框架,那它能做什么呢?

  • 能够发HTTP请求 - Get,Post,Put,Delete,甚至 Head
  • 能够接受HTTP返回,并且能够方便验证其返回值
  • 能够打印所有Log,包含Request和Response的所有部分,这样当Case出错时,我们容易分析问题所在
  • 能够做好数据分离,用配置文件管理测试数据

用到的工具##

显然,框架不是工具,它只是对现有工具的组合和再包装,而这个框架也使用了一些流行的工具:

  • Jersey Client 2.18 我们要使用它来帮助我们发HTTP Request
  • Junit4 测试框架,用它来写Case
  • Apache Commons IO 提供Common API帮助读写文件
  • SLF4J,打印log怎能少了它

如何使用##

最终,所有的HTTP Request都从APIRequest这个类出发,一步步构建,最终调用Invoke方法发送HTTP 请求。

APIResoponse来管理HTTP的返回,这个方法提供一些公共的方法来验证API的返回。

建议所有的TestCase都继承与APITest类这样可以方便的管理配置文件,以及获得一些有用的公共方法。

下面是一些例子:

  1. 如何发一个Get请求

    APIRequest.GET(uri).header("Authorization", token) .invoke().assertStatus(200).assertBodyContains("expectedContent");

  2. 如何使用XML或者Json格式的Payload

    String payload = loadFile("xmlfile.xml");

  3. 如何运行时定制化Payload填充参数

    String payload = String.format(loadFile("jsonfile.json"), "abc", "edf");

  4. 如何做数据分离,在Property文件管理参数

    `String uri = getValue("get.uri");

核心实现##

要想使用Fluent Paragraming Model来写case,那么就得让我们所有的包装方法,都能够返回期望的Class对象,更重要的是,我们是想让Request的返回和验证也能参与到Fluent模式的验证,所以在最终调用方法时,APIRequestAPIResponse就要能和谐的过渡到一起。

所以我们这样定义APIRequest类:

/**
* General Class to make HTTP calls
*
* @author Carl Ji
*/ public class APIRequest { private UriBuilder uri;
private Map<String, String> params = new HashMap<String, String>();
private Map<String, String> headers = new HashMap<String, String>();
private MediaType contentType = MediaType.APPLICATION_XML_TYPE;
private MediaType acceptType;
private String httpMethod;
private String body; private APIRequest(String uri, String method)
{
this.uri=UriBuilder.fromUri(uri);
this.httpMethod = method;
} /**
* Build a HTTP Get request
*
* @param uri
* The URI on which a HTTP get request will be called
* @return
* {@link APIRequest}
*/
public static APIRequest GET(String uri)
{
return new APIRequest(uri, HttpMethod.GET);
} /**
* Build a HTTP Post request
*
* @param uri
* The URI on which a POST request will be called
* @return
* {@link APIRequest}
*/
public static APIRequest POST(String uri)
{
return new APIRequest(uri, HttpMethod.POST);
} /**
* Build a HTTP Put request
*
* @param uri
* The URI on which a PUT request will be called
* @return
* {@link APIRequest}
*/
public static APIRequest PUT(String uri)
{
return new APIRequest(uri, HttpMethod.PUT);
} /**
* Build a HTTP Delete request
*
* @param uri
* The URI that the Delete Request will be called
* @return
* {@link APIRequest}
*/
public static APIRequest DELETE(String uri)
{
return new APIRequest(uri, HttpMethod.DELETE);
} /**
* Build a HTTP HEAD request
*
* @param uri
* The URI that the Head request will be called
* @return
* {@link APIRequest}
*/
public static APIRequest HEAD(String uri)
{
return new APIRequest(uri, HttpMethod.HEAD);
} /**
* Add the {@code value} to the end of URI to build the final URI
*
* @param value
* The value that will be appended to the URI
* @return
* {@link APIRequest}
*/
public APIRequest path(String value)
{
this.uri.path(value);
return this;
} /**
* Build the parameter in the request URI
*
* @param key
* The request URI parameter key
* @param value
* The request URI parameter value
* @return
* {@link APIRequest}
*/
public APIRequest param(String key, String value)
{
params.put(key, value);
return this;
} /**
* Set the content type in the request body
*
* @param type
* The content type {@link MediaType}
* @return
* {@link APIRequest}
*/
public APIRequest type(MediaType type)
{
this.contentType = type;
return this;
} /**
* Set the accepted type for the HTTP response when calling the specific HTTP request
*
* @param type
* The accepted type for the response of this request
* @return
* {@link APIRequest}
*/
public APIRequest accept(MediaType type)
{
this.acceptType = type;
return this;
} /**
* Set the HTTP request headers parameter
*
* @param key
* The header name
* @param value
* The corresponding value for the header
* @return
* {@link APIRequest}
*/
public APIRequest header(String key, String value)
{
headers.put(key, value);
return this;
} /**
* Set the request body
*
* @param body
* The body of the request
* @return
* {@link APIRequest}
*/
public APIRequest body(String body)
{
this.body = body;
return this;
} /**
* Invoke jersey client to send HTTP request
*
* @return {@link APIResponse}
*/
public APIResponse invoke()
{
ClientConfig config = new ClientConfig(); /**
* Important: Jersey Invocation class will check "Entity must be null for http method DELETE."
* so we can not send DELETE request with entity in payload,
* here we suppress this check
*/
config.property(ClientProperties.SUPPRESS_HTTP_COMPLIANCE_VALIDATION, true); Client client = ClientBuilder.newClient(config);
//Print all logs for each request and response
client.register(new LoggingFilter(Logger.getLogger(APIResponse.class.getName()), true)); WebTarget webTarget = client.target(uri);
if(!params.isEmpty())
{
for(Entry<String, String> key: params.entrySet())
{
webTarget = webTarget.queryParam(key.getKey(), key.getValue());
}
} Invocation.Builder invocationBuilder= webTarget.request();
if(acceptType != null)
{
invocationBuilder = invocationBuilder.accept(acceptType);
} if(!headers.isEmpty())
{
for(String key: headers.keySet())
{
invocationBuilder.header(key, headers.get(key));
}
} Response response; if(body == null)
{
response= invocationBuilder.method(httpMethod, Response.class);
}
else
{
response = invocationBuilder.method(httpMethod, Entity.entity(body, contentType), Response.class);
} return new APIResponse(response);
}
}

`

源码地址

源码已上传Github:https://github.com/CarlJi/RestfulAPITests

欢迎大家分享讨论,提意见!

未完待续

下一步打算结合我的Junit Extension工具,给框架添加灵活管理Case的能力,这样当Case变多时,就可以按需执行我们需要的Case。

参考资料##

如果您看了本篇博客,觉得对您有所收获,请点击下面的 [推荐]

如果您想转载本博客,请注明出处大卡的博客[http://www.cnblogs.com/jinsdu/]

如果您对本文有意见或者建议,欢迎留言

Java Fluent Restful API自动化测试框架的更多相关文章

  1. 基于Java+Selenium的WebUI自动化测试框架(一)---页面元素定位器

    对于自动化测试,尤其是UI的自动化测试.是很多做黑盒功能测试的同学,入门自动化测试一个最为直观的或者说最容易理解的途径之一. 对于手工测试和自动化测试的优劣,网上有很多论述,在这里不作展开讨论.但是, ...

  2. Java 调用Restful API接口的几种方式--HTTPS

    摘要:最近有一个需求,为客户提供一些Restful API 接口,QA使用postman进行测试,但是postman的测试接口与java调用的相似但并不相同,于是想自己写一个程序去测试Restful ...

  3. 基于Java+Selenium的WebUI自动化测试框架(十四)-----使用TestNG的Sample

    到目前为止,我们所写的东西,都是集中在如何使用Selenium和Java来定位和读取元素.那么,到底如何具体开展测试,如何实现参数化,如何实现判定呢?下面,我们来看看Java应用程序的测试框架吧. 当 ...

  4. 基于Java+Selenium的WebUI自动化测试框架(八)-----读取元素(XML文件)

    我们继续回到自动化测试框架的主线上来,在前面的文章中,我们定义一个页面元素的主要参数有:路径,找寻方式,等待时间,名称,这个四个参数.另外,我们还需要考虑一个问题,就是网站的页面. 举个例子来说,如果 ...

  5. Slim - 超轻量级PHP Restful API构建框架

    下载源码包: http://www.slimframework.com/ 基于Slim的Restful API Sample: <?php require '/darjuan/Slim/Slim ...

  6. 基于Java+Selenium的WebUI自动化测试框架(十)-----读取Excel文件(JXL)

    之前,我们使用了读取XML文件的方式来实现页面元素的读取,并做成了基础页面类.下面,我们来进行一些扩展,通过Excel来读取页面元素. Excel的使用,大多数人应该都不陌生.那么Java读取Exce ...

  7. 基于Java+Selenium的WebUI自动化测试框架(六)---浏览器初始化

    本篇我们来讨论,如何写一个浏览器初始化的类.在写之前,先思考一下,我们需要一个什么样的初始化? 先来看看使用原生的Java + selenium是怎么做的.(以firefox为例) System.se ...

  8. 基于Java+Selenium的WebUI自动化测试框架(五)------页面操作实现类

    在编写完Log类和监听类之后,终于要回到正轨上来了.我们继续开始写UIExcutor的实现类. PS:如果你想让你的报告更加美观一些.推荐使用reportNG这个jar包. 在项目中导入reportn ...

  9. 基于Java+Selenium的WebUI自动化测试框架(三)------记录LOG

    在有了Position类和接口类之后,我们是不是立刻就要着手开始写实现类了呢?按照一般的顺序是这样.但是,我们这里先停一下.原因有二: 1)既然是写一个框架,我们希望总体的功能上是全面的.实现类中,我 ...

随机推荐

  1. Java - 推断元音辅音

    随机生成字母, 推断是元音字母还是辅音字母. [0,26) + 偏移量'a'就能够生成小写字母. 代码: //: Main.java import java.util.Random; /** * 推断 ...

  2. 在JBoss中部署GeoServer

    GeoServer一直就不能在 JBoss应用服务器中正常部署.最近我在一个国外的论坛上找到了该问题的解决方案.以下方法经测试,可以将GeoServer 2.1.3 成功部署在 JBoss 5.0 和 ...

  3. android 5.0开发环境搭建

    Android 5.0 是 Google 于 2014 年 10 月 15 日发布的全新 Android 操作系统.本文将就最新的Android 5.0 开发环境搭建做详细介绍. 工具/原料 jdk- ...

  4. iOS-SQLite数据库使用介绍

    iOS-SQLite数据库使用介绍 SQLite是MySQL的简化版,更多的运用与移动设备或小型设备上.SQLite的优点是具有可移植性,它不需要服务器就能运行,同时,它也存在一些缺陷,首先,没有提供 ...

  5. 手把手教你从Core Data迁移到Realm

    来源:一缕殇流化隐半边冰霜 (@halfrost ) 链接:http://www.jianshu.com/p/d79b2b1bfa72 前言 看了这篇文章的标题,也许有些人还不知道Realm是什么,那 ...

  6. 20个命令行工具监控 Linux 系统性能(转载)

    1. top — Linux 系统进程监控 top 命令是性能监控程序,它可以在很多 Linux/Unix 版本下使用,并且它也是 Linux 系统管理员经常使用的监控系统性能的工具.Top 命令可以 ...

  7. mac下通过docker搭建LEMP环境

    在mac下通过docker搭建LEMP环境境 1.安装virtualbox.由于docker是在lxc环境的容器 2.安装boot2docker,用于与docker客户端通讯 > brew up ...

  8. float浮动之后高度自适应失效解决方案

    float浮动之后高度自适应失效解决方案 >>>>>>>>>>>>>>>>>>>> ...

  9. Spring MVC 中的 forward 和 redirect

    Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染.假设逻辑视图名为 hello,通过配置,我们配置某个 ViewRes ...

  10. javascript moveTo() 函数

    moveTo-- 移动窗体左上角到相对于屏幕左上角的(x,y)点,当使用负数做为参数时会吧窗体移出屏幕的可视区域 moveTo,中文"移动到"的意思 引用网址:http://www ...