REST API自动化测试(十四)
在REST API自动化测试(十三)中系统详细了介绍了REST Assured框架在API自动化测试中的应用,主要演示了GET的请求方法,
在本文章中,主要详细的介绍REST Assured在POST请求,DELETE,PUT请求以及API自动化测试中针对请求参数的依赖性如何来解
决它。如下是本次需要被测试的API的源码接口,使用的是Flask编写的一个图书管理系统的API管理,源码信息如下:
from flask import Flask,make_response,jsonify,abort,request from flask_restful import Api,Resource from flask_httpauth import HTTPBasicAuth from flask import Flask from flask_jwt import JWT, jwt_required, current_identity from werkzeug.security import safe_str_cmp app=Flask(__name__) app.debug = True app.config[\'SECRET_KEY\'] = \'super-secret\' api=Api(app=app) auth=HTTPBasicAuth() @auth.get_password def get_password(name): if name==\'admin\': return \'admin\' @auth.error_handler def authoorized(): return make_response(jsonify({\'msg\':"请认证"}),403) books=[ {\'id\':1,\'author\':\'wuya\',\'name\':\'Python接口自动化测试实战\',\'done\':True}, {\'id\':2,\'author\':\'无涯\',\'name\':\'Selenium3自动化测试实战\',\'done\':False} ] class User(object): def __init__(self, id, username, password): self.id = id self.username = username self.password = password def __str__(self): return "User(id=\'%s\')" % self.id users = [ User(1, \'wuya\', \'asd888\'), User(2, \'admin\', \'asd888\'), User(3,\'share\',\'asd888\') ] username_table = {u.username: u for u in users} userid_table = {u.id: u for u in users} def authenticate(username, password): user = username_table.get(username, None) if user and safe_str_cmp(user.password.encode(\'utf-8\'), password.encode(\'utf-8\')): return user def identity(payload): user_id = payload[\'identity\'] return userid_table.get(user_id, None) jwt = JWT(app, authenticate, identity) class Books(Resource): # decorators = [auth.login_required] decorators=[jwt_required()] def get(self): return jsonify({\'status\':0,\'msg\':\'ok\',\'datas\':books}) def post(self): if not request.json: return jsonify({\'status\':1001,\'msg\':\'请求参数不是JSON的数据,请检查,谢谢!\'}) else: book = { \'id\': books[-1][\'id\'] + 1, \'author\': request.json.get(\'author\'), \'name\': request.json.get(\'name\'), \'done\': True } books.append(book) return {\'status\':1002,\'msg\': \'添加书籍成功\',\'datas\':book} # return jsonify({\'status\':1002,\'msg\': \'添加书籍成功\',\'datas\':book}, 201) class Book(Resource): # decorators = [auth.login_required] decorators = [jwt_required()] def get(self,book_id): book = list(filter(lambda t: t[\'id\'] == book_id, books)) if len(book) == 0: return jsonify({\'status\': 1003, \'msg\': \'很抱歉,您查询的书的信息不存在\'}) else: return jsonify({\'status\': 0, \'msg\': \'ok\', \'datas\': book}) def put(self,book_id): book = list(filter(lambda t: t[\'id\'] == book_id, books)) if len(book) == 0: return jsonify({\'status\': 1003, \'msg\': \'很抱歉,您查询的书的信息不存在\'}) elif not request.json: return jsonify({\'status\': 1001, \'msg\': \'请求参数不是JSON的数据,请检查,谢谢!\'}) elif \'author\' not in request.json: return jsonify({\'status\': 1004, \'msg\': \'请求参数author不能为空\'}) elif \'name\' not in request.json: return jsonify({\'status\': 1005, \'msg\': \'请求参数name不能为空\'}) elif \'done\' not in request.json: return jsonify({\'status\': 1006, \'msg\': \'请求参数done不能为空\'}) elif type(request.json[\'done\'])!=bool: return jsonify({\'status\': 1007, \'msg\': \'请求参数done为bool类型\'}) else: book[0][\'author\'] = request.json.get(\'author\', book[0][\'author\']) book[0][\'name\'] = request.json.get(\'name\', book[0][\'name\']) book[0][\'done\'] = request.json.get(\'done\', book[0][\'done\']) return jsonify({\'status\': 1008, \'msg\': \'更新书的信息成功\', \'datas\': book}) def delete(self,book_id): book = list(filter(lambda t: t[\'id\'] == book_id, books)) if len(book) == 0: return jsonify({\'status\': 1003, \'msg\': \'很抱歉,您查询的书的信息不存在\'}) else: books.remove(book[0]) return jsonify({\'status\': 1009, \'msg\': \'删除书籍成功\'}) api.add_resource(Books,\'/v1/api/books\') api.add_resource(Book,\'/v1/api/book/<int:book_id>\') if __name__ == \'__main__\': app.run(debug=True)
针对API之间的上下依赖的接口信息,说的更加具体点就是上一个API接口的输出是下一个API接口的输入信息,这里的场景在API
的自动化测试中其实是非常常见的,也是很普通的场景。针对登录成功后的TOKEN信息的获取思路其实都一样的,那么再具体点就是
我们把登录成功后的TOKEN信息获取到,存储到一个文件中,在下一个接口中我们再可以把TOKEN信息读取出来,作为下一个API接
口的请求头输入信息。依据API测试用例的编写规则,每个API测试用例都是独立的,不可相互依赖的,比如说测试一个查看书籍的信
息接口,那么首先就得先添加,然后再查看,最后删除,这样的一个测试场景它是比较完整的。依据这样的思路,可以把添加书籍,删
除书籍信息都编写成独立的方法,这样就可以很轻松的实现每个API测试用例的独立性。下面的测试代码是针对图书管理系统的测试
代码,源码如下:
package rest.client.api.test; /* * */ import io.restassured.http.Method; import io.restassured.path.json.JsonPath; import io.restassured.response.Response; import io.restassured.response.ResponseBody; import io.restassured.RestAssured; import io.restassured.specification.RequestSpecification; import org.apache.poi.hssf.record.SSTRecord; import org.json.JSONArray; import org.json.JSONObject; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class test_login_auth { @BeforeMethod public void setUp() { RestAssured.baseURI="http://127.0.0.1:5000"; } /* * 操作文件IO进行重构 * */ public void write(String fileName,String content) { File file=new File(fileName); try{ FileWriter fileWriter=new FileWriter(file); fileWriter.write(content); fileWriter.close(); }catch(IOException e){ e.printStackTrace(); } } public String read(String fileName) { String content=null; File file=new File(fileName); try{ FileReader fileReader=new FileReader(file); char byt[]=new char[1024]; content=new String(byt,0,fileReader.read(byt)); }catch(IOException e){ e.printStackTrace(); } return content; } @Test public void test_auth_success() { RequestSpecification httpRequest=RestAssured.given(); JSONObject requestParams=new JSONObject(); requestParams.put("username","wuya"); requestParams.put("password","asd888"); httpRequest.header("Content-Type","application/json"); httpRequest.body(requestParams.toString()); //发送请求 Response response=httpRequest.request(Method.POST,"/auth"); JsonPath jsonPath= response.jsonPath(); String token=jsonPath.get("access_token"); this.write("token",token); } @Test(dependsOnMethods = "test_auth_success",description = "获取所有书籍信息") public void test_get_all_books() { RequestSpecification httpRequest=RestAssured.given(); String s4="jwt %s"; httpRequest.header("Authorization",String.format(s4,this.read("token"))); Response response=httpRequest.request(Method.GET,"/v1/api/books"); JsonPath jsonPath=response.jsonPath(); String msg=jsonPath.get("msg"); Assert.assertEquals(msg,"ok"); Assert.assertEquals(response.getStatusCode(),200); } @Test(description = "添加书籍信息",dependsOnMethods = "test_auth_success") public void test_add_book() { Response response=this.addBook(); String jsonString=response.body().asString(); Integer bookID=com.jayway.jsonpath.JsonPath.read(jsonString,"$.datas.id"); String s5="%s"; this.write("bookID.txt",String.format(s5,bookID)); } @Test(description = "查看新增的书籍信息") public void test_get_book_one() { //新增书籍 this.addBook(); RequestSpecification httpRequest=RestAssured.given(); String s4="jwt %s"; //请求头的信息 httpRequest.header("Authorization",String.format(s4,this.read("token"))); String s5="/v1/api/book/%s"; Response response=httpRequest.request(Method.GET,String.format(s5,this.read("bookID.txt"))); String jsonString=response.body().asString(); String name= com.jayway.jsonpath.JsonPath.read(jsonString,"$.datas[0].name"); Assert.assertEquals(response.getStatusCode(),200); Assert.assertEquals(name,"Python测试实战"); this.deleteBook(); } @Test(description = "修改书籍的信息") public void test_set_book() { //新增书籍 this.addBook(); RequestSpecification httpRequest=RestAssured.given(); JSONObject requestParams = new JSONObject(); requestParams.put("author","wuya"); requestParams.put("name","Python实战"); requestParams.put("done",true); httpRequest.body(requestParams.toString()); httpRequest.header("Content-Type","application/json"); String s4="jwt %s"; httpRequest.header("Authorization",String.format(s4,this.read("token"))); String s5="/v1/api/book/%s"; Response response=httpRequest.request(Method.PUT,String.format(s5,this.read("bookID.txt"))); String jsonString=response.body().asString(); String name= com.jayway.jsonpath.JsonPath.read(jsonString,"$.datas[0].name"); Assert.assertEquals(response.getStatusCode(),200); JsonPath jsonPath=response.jsonPath(); int code=jsonPath.get("status"); Assert.assertEquals(code,1008); Assert.assertEquals(name,"Python实战"); //删除书籍 this.deleteBook(); } @Test(description = "删除书籍信息",dependsOnMethods = {"test_auth_success","test_add_book"}) public void test_delete_book() { Response response=this.deleteBook(); JsonPath jsonPath=response.jsonPath(); System.out.println(response.body().asString()); Assert.assertEquals(response.getStatusCode(),200); int code=jsonPath.get("status"); Assert.assertEquals(code,1009); } public Response addBook() { RequestSpecification httpRequest=RestAssured.given(); JSONObject jsonObject=new JSONObject(); jsonObject.put("author","wuya"); jsonObject.put("name","Python测试实战"); jsonObject.put("done",true); //处理请求的参数 httpRequest.body(jsonObject.toString()); //添加请求头的信息 String s4="jwt %s"; httpRequest.header("Content-Type","application/json"); httpRequest.header("Authorization",String.format(s4,this.read("token"))); //发送请求 Response response=httpRequest.request(Method.POST,"/v1/api/books"); return response; } public Response deleteBook() { RequestSpecification httpRequest=RestAssured.given(); String s4="jwt %s"; httpRequest.header("Authorization",String.format(s4,this.read("token"))); //发送请求 String s5="/v1/api/book/%s"; Response response=httpRequest.request(Method.DELETE,String.format(s5,this.read("bookID.txt"))); return response; } }
针对上面的代码特别需要注意的是就是我们针对删除书籍和添加书籍写成了单独的方法,但是我们最终会返回的是Response的对象,
这点需要特别的注意,因为只有拿到了Response的对象,我们就可以再得到响应状态码,响应数据等信息。如上的其他案例代码
这里就不做详细的解释了,了解Java代码的同学看了都知道怎么处理,一言以蔽之。
感谢您的阅读,后续会持续更新!