rest-assured使用

前面介紹過了rest-assured-wiki翻譯,這篇我們來實戰使用下

版本選用4.2.0,目前最新的是4.3.0,但有groovy版本bug。(在使用開源組件時,一定要注意最新版的風險)

使用4.3.0時遇到的bug AbstractMethodError

在使用開源組件時,遇到bug,可以第一時間去對應的issues裏查看下,有時比baidu/google效率高的多

常用的鏈接

1.常用語法組合

  • given()--when()--then()
  • given()--expect()--when()

given裏設置參數、頭、認證
when()裏請求rest接口,get、post、put、delete等
expect()then()裏驗證結果。

given()–expect()–when()

given().
        param("x", "y").
expect().
        statusCode(400).
        body("lotto.lottoId", equalTo(6)).
when().
        get("/lotto");

given()–when()–then()

given().
        param("x", "y").
when().
        get("/lotto").
then().
        statusCode(400).
        body("lotto.lottoId", equalTo(6));

方法調用鏈圖

在這裏插入圖片描述
看不懂這圖,可以先不用急,看完下面rest-assured原生的api如何使用後,再來看這張圖可能對方法間調用關係就明白了。

爲了更好的演示,創建個springboot的web項目,爲了簡化,只有controller層和domain層,沒有service和dao的邏輯。

如何創建springboot項目,就不多介紹了,不會可以參照 iworkh-springboot-helloworld

1.業務代碼

1-1.交互類

JsonDataResult類主要爲了將返回數據封裝成統一固定格式,返回。

public class JsonDataResult<T> {

    protected boolean success;
    protected String message;
    protected int errorCode = 0;

    @JsonProperty("result")
    protected T data;
    
    ...省略了setgeter...
}

1-2.實體類

public class UserVo {

    private int id;

    private String name;

    private Date birthday;

    private boolean vip;
        
    ...省略了setgeter...
}

不多解釋,一個bean

1-3.controller

UserController類裏定義了增刪改查接口,並@RestController註解,返回值是json

@RestController
@RequestMapping("/api/user")
public class UserController {

    @PostMapping("/createUserByParam")
    public JsonDataResult<Boolean> createUserByParam(UserVo userVo) {
        JsonDataResult<Boolean> result = new JsonDataResult<>();
        userVo.setId(new Random().nextInt(50));
        result.setSuccess(true);
        result.setData(true);
        return result;
    }

    @PostMapping("/createUserByJson")
    public JsonDataResult<Boolean> createUserByJson(@RequestBody UserVo userVo) {
        JsonDataResult<Boolean> result = new JsonDataResult<>();
        userVo.setId(new Random().nextInt(100));
        result.setSuccess(true);
        result.setData(true);
        return result;
    }

    @GetMapping("/{id}")
    public JsonDataResult<UserVo> getUser(@PathVariable int id) {
        JsonDataResult<UserVo> result = new JsonDataResult<>();
        String[] hobbies = {"football", "sing"};
        //從數據庫查詢,省略
        UserVo user = new UserVo(id, "iworkh" + id, System.currentTimeMillis(), true, Arrays.asList(hobbies));
        result.setSuccess(true);
        result.setData(user);
        return result;
    }

    @PutMapping
    public JsonDataResult<UserVo> updateUser(@RequestBody UserVo userVo) {
        JsonDataResult<UserVo> result = new JsonDataResult<>();
        //從數據庫刪除,省略
        result.setSuccess(true);
        result.setData(userVo);
        return result;
    }

    @DeleteMapping("/{id}")
    public JsonDataResult<Boolean> delteUser(@PathVariable int id) {
        JsonDataResult<Boolean> result = new JsonDataResult<>();
        //從數據庫刪除,省略
        result.setSuccess(true);
        result.setData(true);
        return result;
    }
}

2.原生rest-assured API

RestAssured類是很關鍵的一個類,打開發現很多方法,和默認配置,這裏提下幾個默認配置

  • DEFAULT_URI: 默認值是 http://localhost
  • DEFAULT_PORT: 默認值是 8080

所以,當我們端口號不是默認值時,我們也得修改配置。(比如:RestAssured.port = 9090;)

爲了簡單演示,一些認證都不打開了。直接演示調用rest接口測試。

引入依賴

<dependencies>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>rest-assured</artifactId>
        <version>${rest-assured.version}</version>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <artifactId>json-path</artifactId>
                <groupId>io.rest-assured</groupId>
            </exclusion>
            <exclusion>
                <artifactId>xml-path</artifactId>
                <groupId>io.rest-assured</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>json-path</artifactId>
        <version>${rest-assured.version}</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.rest-assured</groupId>
        <artifactId>xml-path</artifactId>
        <version>${rest-assured.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

其中rest-assured的版本是4.2.0<rest-assured.version>4.2.0</rest-assured.version>

2-1.插入

2-1-1.queryParams方式

queryParams:即通過url後面加參數方式傳遞

@Test
public void test001CreateUserByUrl() {
    String[] hobbies = {"football", "sing"};
    UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));

    given().
           queryParams(BeanMapTool.beanToMap(zhangsanUser)).
    when().
           post("/api/user/createUserByParam").
    then().
           statusCode(200).
           body("result", equalTo(true));
}

XxxParmas方法需要的是一個Map對象,所以可以使用Map初始化值傳遞。
BeanMapTool.beanToMap()是個工具類,可以將bean轉化爲Map,使用的是org.springframework.cglib.beans.BeanMap來完成,文章最後會給出此工具類

2-1-2.params方式

queryParams:即通過post參數方式

@Test
public void test001CreateUserByParam() {
    String[] hobbies = {"football", "sing"};
    UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));

    given().
            params(BeanMapTool.beanToMap(zhangsanUser)).
    when().
            post("/api/user/createUserByParam").
    then().
            statusCode(200).
            body("result", equalTo(true));
}

2-1-3.formParams方式

formParams:即通過post form表單方式

@Test
public void test001CreateUserByFormParam() {
    String[] hobbies = {"football", "sing"};
    UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));

    given().
           formParams(BeanMapTool.beanToMap(zhangsanUser)).
    when().
           post("/api/user/createUserByParam").
    then().
           statusCode(200).
           body("result", equalTo(true));
}

2-1-4.json方式

contentType:指定json格式,並body傳數據。(body的值,不一定json字符串,是對象也可以)

@Test
public void test003CreateUserByJson() {
    String[] hobbies = {"football", "sing"};
    UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));

    given().
            contentType(ContentType.JSON).body(zhangsanUser).
    when().
           post("/api/user/createUserByJson").
    then().
           statusCode(200).
           body("result", equalTo(true));
}

2-2.更新

@Test
public void test002Update(){
    String[] hobbies = {"football", "play games"};
    UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));

    given().
            contentType(ContentType.JSON).body(zhangsanUser).
    when().
            put("/api/user/").
    then().
            statusCode(200).
            body("result.hobbies", hasItems("football", "play games"));
}

更新比較簡單,使用put來更新,驗證使用hasItems來驗證多個結果

2-3.刪除

@Test
public void test003Delete(){
    given().
    when().
        delete("/api/user/{id}",1).
    then().
        statusCode(200).
        body("result", equalTo(true));
}

刪除更簡單,使用delete

2-4.查詢

查詢留最後,因爲這時我們使用最多,而且使用技巧最多的地方

2-4-1.body驗證

@Test
public void test004GetUserPathParam() {
    given().
           pathParam("id", 1).
    when().
          get("/api/user/{id}").
    then().
           statusCode(200).
           body("result.name", equalTo("iworkh1"));
}

直接通過body的path提交值,驗證

2-4-2.Response值驗證

@Test
public void test004GetUserExactResponse() {
    Response response=
        given().
        expect().
                statusCode(200).
        when().
                get("/api/user/2");

    String name = response.path("result.name");
    Assert.assertThat(name, equalTo("iworkh2"));
}

直接根據Response的返回值,自己解析Response裏header、body等來驗證

2-4-3.轉化對象

我們還可以直接將Response轉化爲對象,來驗證處理

通過Response的as方法,參數是類型

  • 類型是普通類: Xxx.class即可
  • 類型是泛型:new TypeRef<Xxx>(){}來轉化爲需要的對象
@Test
public void test004GetUserToBean() {
    Response response = RestAssuredTool.get("/api/user/3");
    JsonDataResult<UserVo> userVoJsonResult = response.as(new TypeRef<JsonDataResult<UserVo>>() {});
    System.out.println(userVoJsonResult.getData());
    Assert.assertThat(userVoJsonResult.getData().getName(), equalTo("iworkh3"));
}

3.封裝rest-assured API

原生rest-assured API使用起來,非常的靈活,但是對於開發者而言,一直...(Fluent風格)的方法調用比較麻煩。

我比較傾向於,調用一個方法,把需要參數都傳過去,就結束了,不需要關係底層太多調用,因此對原生的API做下封裝

封裝好的好處

  • 認證在封裝裏做,不用在測試代碼中去驗證
  • 業務測試代碼更關注業務,而不用太關注rest-assured的使用

缺點

  • 被封裝後的方法,不夠靈活。(不靈活,那就原生API,只要留出接口就行)

文章最後,給出封裝好的RestAssuredTool類,當然這封裝的不一定滿足所有場合,也不是最好的。(大家可根據自己的需求來封裝,這隻拋磚引玉下)

調用封裝後的測試類

package com.iworkh.test.restassured.controller;

import com.iworkh.test.restassured.domain.vo.JsonDataResult;
import com.iworkh.test.restassured.domain.vo.UserVo;
import com.iworkh.test.restassured.utils.BeanMapTool;
import com.iworkh.test.restassured.utils.RestAssuredTool;
import io.restassured.common.mapper.TypeRef;
import io.restassured.response.Response;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;

import java.util.Arrays;
import java.util.Map;

import static org.hamcrest.Matchers.equalTo;

/**
 * UserController測試類
 *
 * @author: iworkh-沐雨雲樓
 * @date: 2020-06-18
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class UserControllerTest {

    private String userBaseUrl = "/api/user";

    @Test
    public void test001CreateUserByParam() {
        String[] hobbies = {"football", "sing"};
        UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));
        Response resp = RestAssuredTool.postWithParams(userBaseUrl + "/createUserByParam",
                                                       BeanMapTool.beanToMap(zhangsanUser));

        RestAssuredTool.validateStatusCode(resp, 200);
        RestAssuredTool.validateEqualTo(resp, "result", true);
    }

    @Test
    public void test001CreateUserByJson() {
        String[] hobbies = {"football", "sing"};
        UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));
        Response resp = RestAssuredTool.postWithJson(userBaseUrl + "/createUserByJson", zhangsanUser);
        RestAssuredTool.validateStatusCode(resp, 200);
    }

    @Test
    public void test002Upate() {
        String[] hobbies = {"football", "play games"};
        UserVo zhangsanUser = new UserVo(1, "zhangsan", System.currentTimeMillis(), false, Arrays.asList(hobbies));
        Response response = RestAssuredTool.putWithJson(userBaseUrl, zhangsanUser);
        RestAssuredTool.validateStatusCode(response, 200);
    }

    @Test
    public void test003Delete() {
        Response response = RestAssuredTool.delete(userBaseUrl + "/id");
        RestAssuredTool.validateStatusCode(response, 200);
    }

    @Test
    public void test004GetUser01() {
        Response resp = RestAssuredTool.get(userBaseUrl + 1);
        RestAssuredTool.validateHasItems(resp, "result.hobbies", "football", "sing");
    }

    @Test
    public void test004GetUser02() {
        Response response = RestAssuredTool.get(userBaseUrl + 2);
        int id = response.path("result.id");
        Assert.assertThat(id, equalTo(2));
    }

    @Test
    public void test004GetUser03() {
        Response response = RestAssuredTool.get(userBaseUrl + 3);
        // 轉化爲JsonDataResult對象,不過data部分是map,再使用BeanMapTool工具可以轉爲對對應的對象
        JsonDataResult<Map<String, ?>> userVoJsonDataResult = RestAssuredTool.asJsonDataResult(response);

        System.out.println(userVoJsonDataResult.getData());
        Assert.assertThat(userVoJsonDataResult.getData().get("name"), equalTo("iworkh3"));

        try {
            UserVo userVo = BeanMapTool.mapToBean(userVoJsonDataResult.getData(), UserVo.class);
            Assert.assertThat(userVo.getName(), equalTo("iworkh3"));
        } catch (IllegalAccessException | InstantiationException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void test004GetUser04() {
        Response response = RestAssuredTool.get(userBaseUrl + 4);
        // 通過泛型轉化爲需要的對象
        JsonDataResult<UserVo> userVoJsonResult = RestAssuredTool.asGeneric(response,
                                                                            new TypeRef<JsonDataResult<UserVo>>() {
                                                                            });
        Assert.assertThat(userVoJsonResult.getData().getName(), equalTo("iworkh4"));
    }
}

代碼中重要的地方都有註釋,就不多解釋了

4.工具類

4-1.BeanMapTool

bean轉map工具類

參照博客 工具類–bean和map互轉

使用的是裏面的BeanMapTool工具類

4-2.RestAssuredTool

需要擴展的幾個點:

  • port和baseURI修改成從配置文件讀取
  • 在初始化restClient時,將認證加上

其他如何操作可以查看官網或者查看翻譯的wiki rest-assured wiki翻譯

package com.iworkh.test.restassured.utils;

import com.iworkh.test.restassured.domain.vo.JsonDataResult;
import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.http.ContentType;
import io.restassured.http.Headers;
import io.restassured.path.json.JsonPath;
import io.restassured.path.json.config.JsonPathConfig;
import io.restassured.response.Response;
import io.restassured.specification.RequestSpecification;

import java.util.Map;

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

/**
 * RestAssuredTool工具類
 *
 * @author: iworkh-沐雨雲樓
 * @date: 2020-06-18
 */
public class RestAssuredTool {

    static {
        // 這可以修改爲從配置文件讀取
        String siteBaseURI = "http://localhost";
        int port = 8080;
        RestAssured.baseURI = siteBaseURI;
        RestAssured.port = port;
        JsonPath.config = new JsonPathConfig("UTF-8");
    }

    public static RequestSpecification restClient() {
        // 認證等操作,都可以在這統一處理
        return given();
    }

    public static RequestSpecification restClientWithHeader(Headers headers) {
        // 認證等操作,都可以在這統一處理
        return given().headers(headers);
    }

    // get
    public static Response get(String url) {
        return restClient().get(url);
    }

    public static Response getWithParams(String url, Map<String, ?> params) {
        return restClient().params(params).get(url);
    }

    public static Response getWithQueryParams(String url, Map<String, ?> params) {
        return restClient().queryParams(params).get(url);
    }

    public static Response getWithFormParams(String url, Map<String, ?> params) {
        return restClient().formParams(params).get(url);
    }

    public static <T> Response getWithJson(String url, T data) {
        return restClient().contentType(ContentType.JSON).body(data).get(url);
    }

    // post
    public static Response postWithParams(String url, Map<String, ?> params) {
        return restClient().params(params).post(url);
    }

    public static Response postWithFormParams(String url, Map<String, ?> params) {
        return restClient().formParams(params).post(url);
    }

    public static <T> Response postWithJson(String url, T data) {
        return restClient().contentType(ContentType.JSON).body(data).post(url);
    }

    // put
    public static Response putWithParams(String url, Map<String, ?> params) {
        return restClient().params(params).put(url);
    }

    public static Response putWithFormParams(String url, Map<String, ?> params) {
        return restClient().formParams(params).put(url);
    }

    public static <T> Response putWithJson(String url, T data) {
        return restClient().contentType(ContentType.JSON).body(data).put(url);
    }

    // delete
    public static Response delete(String url) {
        return restClient().delete(url);
    }

    public static <T> Response deleteWithJson(String url, T data) {
        return restClient().contentType(ContentType.JSON).body(data).delete(url);
    }

    // validate response
    public static void validateStatusCode(Response response, int expectedStatusCode) {
        response.then().statusCode(expectedStatusCode);
    }

    public static <T> void validateEqualTo(Response response, String path, T expectedValue) {
        response.then().body(path, equalTo(expectedValue));
    }

    public static <T> void validateHasItems(Response response, String path, T... expectedValue) {
        response.then().body(path, hasItems(expectedValue));
    }

    // convert
    public static JsonDataResult<Map<String, ?>> asJsonDataResult(Response response) {
        return response.as(new TypeRef<JsonDataResult<Map<String, ?>>>() {});
    }

    public static <T> T asGeneric(Response response, TypeRef<T> typeRef) {
        return response.as(typeRef);
    }

    public static <T> T asCls(Response response, Class<T> cls) {
        return response.as(cls);
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章