1. 程式人生 > >就是這麼簡單!使用Rest-assured 測試Restful Web Services

就是這麼簡單!使用Rest-assured 測試Restful Web Services



使用 Rest-assured 測試 Restful Web Services

這裡向大家介紹一個測試Restful web service 的框架,叫Rest-assured.

他提供了一系列好的功能,像DSL式的語法, XPath-Validate,  檔案上傳,Specification重用, 使用代理, Spring MVC mock module測試Controllers等等,讓你在Java裡面測試Rest service 和那些動態語言Ruby, Groovy一樣靈活。

目錄
       1. 前提
       2. 配置
       3. Example詳解
       4. Troubleshooting
       5. 參考來源

前提條件

  • JDK >= 1.6
  • Maven 3

配置Maven工程pom檔案如下

<dependency>

<groupId>com.jayway.restassured</groupId>

<artifactId>rest-assured</artifactId>

<version>2.3.3</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

<scope>test</scope>

</dependency>

Example

a)  測試一個GET 請求方法,

請求URL : http://10.46.28.193:8080/service/v1/user/login

返回JSON內容如下

{
  "userInfo": {
    "password": null,
    "userId": "wadexu",
    "accessSecurityCodes
": "10000000000000000000", "firstName": "Wade", "lastName": "Xu", "status": 8, "officePhone": "58730", "email": "[email protected]", "homePhone": "123" }, "success": true, "error": null }

測試程式碼如下:

@Before
public void setUp() {
    RestAssured.baseURI= "http://10.46.28.193";
    RestAssured.port = 8080;
    RestAssured.basePath = "/service/v1";
}

@Test
public void testUserLogin() {
  expect().
    statusCode(200).
    body(
      "success", equalTo(true),
      "userInfo.userId", equalTo("wadexu"),
      "userInfo.firstName", equalTo("Wade"),
      "userInfo.lastName", equalTo("Xu"),
      "error", equalTo(null)).
    when().
    get("/user/login?userName=wadexu&password=NzrmRcIfIW4=");
}

注意我這裡請求時的引數直接塞進了URL裡, 稍後會講到如何指明引數。

b) 如何使用JSON path

還是同上面的例子, 測試程式碼如下:

@Test
public void testUserLogin_JsonPath() {
  Response response = get("/user/login?userName=wadexu&password=NzrmRcIfIW4=");
  assertEquals(200, response.getStatusCode());
  String json = response.asString();
  JsonPath jp = new JsonPath(json);
  assertEquals("wadexu", jp.get("userInfo.userId"));
  assertEquals("Wade", jp.get("userInfo.firstName"));
  assertEquals("Xu", jp.get("userInfo.lastName"));
  assertEquals("123", jp.get("userInfo.homePhone"));
}

c) 如何使用引數

Get請求是用queryParam, 如果你直接寫 param,在這個case裡也可以,Rest Assured 會自動判斷引數型別(query or form parameter), 在有些case裡, Put 或 Post 你得指明引數型別

    @Test
public void testUserLogin_Parameter() {
    final String userName = "wadexu";
    final String password = "NzrmRcIfIW4=";
    given().
    queryParam("userName", userName).queryParam("password", password).
    expect().
    statusCode(200).
    body("success", equalTo(true), 
      "userInfo.userId", equalTo("wadexu"), 
      "userInfo.firstName", equalTo("Wade"), 
      "userInfo.lastName", equalTo("Xu"), 
      "error", equalTo(null)).when()
    .get("/user/login");
}

另外,有些Post 請求URL後面是有引數的, 這時候 你可以這樣寫

post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);

或者

given().
        pathParam("hotelId", "My Hotel").
        pathParam("roomNumber", 23).
when(). 
        post("/reserve/{hotelId}/{roomNumber}").
then().
         ..

d) 再來看一個POST 請求, 這時候需要請求訊息體body了,request body是JSON體如下:

{

"customerId": "CDICC",

"broker": "test",

"editUserId": "wadexu"

}

測試程式碼:

    @Test
public void testCreate() {
    final String bodyString = "{\"customerId\": \"CDICC\",\"broker\": \"test\",\"editUserId\": \"wadexu\"}";
   
    given().
    contentType("application/json").
    request().body(bodyString).
    expect().
      statusCode(200).
      body(
      "order.orderNumber", is(Number.class),
      "order.deleteDate", is(nullValue()),
      "success", equalTo(true)).
    when().
    post("/order");
}

這時除了用到request().body

還多加了一個header 請求訊息頭 -- ContentType

set Headers 的方法有很多, 上面是其一, 你還可以按如下方式做:

given().header("Content-Type", "application/json")
given().headers("Accept", "application/json", "Content-Type", "application/json")

另外 注意到期望結果的比較沒有, 這裡用到org.hamcrest.Matchers的一些方法, 因為Order number 每次不一樣,無法判斷具體是多少,所以就看是否是數字就行了,刪除日期是null value

hamcrest.Matchers 裡的各種匹配器有興趣的童鞋可以研究下, 對測試斷言很有幫助。

e) 同樣你還可以verify HTTP Status code

因為我這個service是需要Content-Type=application/json的, 而我的case裡並沒有賦值給contentType, 所以返回會報錯 415

The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.

 @Test
public void testOpenOrder_error() {
  final String orderNumber = "3017";
  final String orderVersion = "1";
  final String versionType = "";
  final String editUserId = "";
  final String customerId = "";
  final String state = "";
  given().
  parameters(
    "orderNumber", orderNumber,
    "orderVersion", orderVersion,
    "versionType", versionType,
    "editUserId", editUserId,
    "customerId", customerId,
    "state", state).
  expect().
    statusCode(415).
  when().
  post("/order/open");
}

f) Cookies 其實都大同小異了

第一個沒有set cookie 結果拋 403

"name":" Forbidden ",

"detail":"The request was a legal request, but the server is refusing to respond to it. Unlike a 401 Unauthorized response, authenticating will make no difference."

@Test
public void testCookie() {
  expect().
    statusCode(403).
  when().
  get("/access");
 
  given().
    cookie("userName", "wadexu").
  expect().
    statusCode(200).
  when().
  get("/access");
}

g) Authentication

如果你的service需要認證,則需要設定authentication()

否則401 -- Unauthorized

@Test
public void testAuthentication() {
  expect().
  statusCode(401).
  when().
  get("/service/user");
 
  expect().
  statusCode(200).
  when().
  with().
    authentication().basic("wadexu", "123456").
  get("/service/user");
}

H) Specification reuse 規範重用

 @Test
  public void testSpecReuse() {
    ResponseSpecBuilder builder = new ResponseSpecBuilder();
    builder.expectStatusCode(200);
    builder.expectBody("userInfo.userId", equalTo("wadexu"));
    builder.expectBody("userInfo.firstName", equalTo("Wade"));
    builder.expectBody("userInfo.lastName", equalTo("Xu"));
    builder.expectBody("success", equalTo(true));
    ResponseSpecification responseSpec = builder.build();
    //use this specification for test example -- a
    expect().
      spec(responseSpec).
    when().
    get("/user/login?userName=wadexu&password=NzrmRcIfIW4=");
    //now re-use for another example -- c that returns similar data 
    given().
      queryParam("userName", "wadexu").
      queryParam("password", "NzrmRcIfIW4=").
    expect().
      spec(responseSpec).
    when().
    get("/user/login");
  }

如果你還有更多的測試,返回期望結果又類似 則可以繼續使用 specification, 達到重用的目的。

測試執行

Troubleshooting

有些類需要Static imports

參考我的如下:

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import com.jayway.restassured.RestAssured;
import com.jayway.restassured.builder.ResponseSpecBuilder;
import com.jayway.restassured.path.json.JsonPath;
import com.jayway.restassured.response.Response;
import com.jayway.restassured.specification.ResponseSpecification;

import static com.jayway.restassured.RestAssured.*;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.*;

設定好你的請求url 路徑, 預設http://localhost:8080

參考我的base path(即所以請求url 前面相同的部分) 配置如下:

@Before
    public void setUp() {
        RestAssured.baseURI= "http://10.46.28.193";
        RestAssured.port = 8080;
        RestAssured.basePath = "/service/v1";
    }

“WARNING: Cannot find parser for content-type: text/json — using default parser.”

– 需要註冊相關的parser: e.g. RestAssured.registerParser(“text/json”, Parser.JSON);

參考來源

使用 Rest-assured 測試 Restful Web Services

這裡向大家介紹一個測試Restful web service 的框架,叫Rest-assured.

他提供了一系列好的功能,像DSL式的語法, XPath-Validate,  檔案上傳,Specification重用, 使用代理, Spring MVC mock module測試Controllers等等,讓你在Java裡面測試Rest service 和那些動態語言Ruby, Groovy一樣靈活。

目錄
       1. 前提
       2. 配置
       3. Example詳解
       4. Troubleshooting
       5. 參考來源

前提條件

  • JDK >= 1.6
  • Maven 3

配置Maven工程pom檔案如下

<dependency>

<groupId>com.jayway.restassured</groupId>

<artifactId>rest-assured</artifactId>

<version>2.3.3</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

<scope>test</scope>

</dependency>

Example

a)  測試一個GET 請求方法,

請求URL : http://10.46.28.193:8080/service/v1/user/login

返回JSON內容如下

{
  "userInfo": {
    "password": null,
    "userId": "wadexu",
    "accessSecurityCodes": "10000000000000000000",
    "firstName": "Wade",
    "lastName": "Xu",
    "status": 8,
    "officePhone": "58730",
    "email": "[email protected]",
    "homePhone": "123"
  },
  "success": true,
  "error": null
}

測試程式碼如下:

@Before
public void setUp() {
    RestAssured.baseURI= "http://10.46.28.193";
    RestAssured.port = 8080;
    RestAssured.basePath = "/service/v1";
}

@Test
public void testUserLogin() {
  expect().
    statusCode(200).
    body(
      "success", equalTo(true),
      "userInfo.userId", equalTo("wadexu"),
      "userInfo.firstName", equalTo("Wade"),
      "userInfo.lastName", equalTo("Xu"),
      "error", equalTo(null)).
    when().
    get("/user/login?userName=wadexu&password=NzrmRcIfIW4=");
}

注意我這裡請求時的引數直接塞進了URL裡, 稍後會講到如何指明引數。

b) 如何使用JSON path

還是同上面的例子, 測試程式碼如下:

@Test
public void testUserLogin_JsonPath() {
  Response response = get("/user/login?userName=wadexu&password=NzrmRcIfIW4=");
  assertEquals(200, response.getStatusCode());
  String json = response.asString();
  JsonPath jp = new JsonPath(json);
  assertEquals("wadexu", jp.get("userInfo.userId"));
  assertEquals("Wade", jp.get("userInfo.firstName"));
  assertEquals("Xu", jp.get("userInfo.lastName"));
  assertEquals("123", jp.get("userInfo.homePhone"));
}

c) 如何使用引數

Get請求是用queryParam, 如果你直接寫 param,在這個case裡也可以,Rest Assured 會自動判斷引數型別(query or form parameter), 在有些case裡, Put 或 Post 你得指明引數型別

    @Test
public void testUserLogin_Parameter() {
    final String userName = "wadexu";
    final String password = "NzrmRcIfIW4=";
    given().
    queryParam("userName", userName).queryParam("password", password).
    expect().
    statusCode(200).
    body("success", equalTo(true), 
      "userInfo.userId", equalTo("wadexu"), 
      "userInfo.firstName", equalTo("Wade"), 
      "userInfo.lastName", equalTo("Xu"), 
      "error", equalTo(null)).when()
    .get("/user/login");
}

另外,有些Post 請求URL後面是有引數的, 這時候 你可以這樣寫

post("/reserve/{hotelId}/{roomNumber}", "My Hotel", 23);

或者

given().
        pathParam("hotelId", "My Hotel").
        pathParam("roomNumber", 23).
when(). 
        post("/reserve/{hotelId}/{roomNumber}").
then().
         ..

d) 再來看一個POST 請求, 這時候需要請求訊息體body了,request body是JSON體如下:

{

"customerId": "CDICC",

"broker": "test",

"editUserId": "wadexu"

}

測試程式碼:

    @Test
public void testCreate() {
    final String bodyString = "{\"customerId\": \"CDICC\",\"broker\": \"test\",\"editUserId\": \"wadexu\"}";
   
    given().
    contentType("application/json").
    request().body(bodyString).
    expect().
      statusCode(200).
      body(
      "order.orderNumber", is(Number.class),
      "order.deleteDate", is(nullValue()),
      "success", equalTo(true)).
    when().
    post("/order");
}

這時除了用到request().body

還多加了一個header 請求訊息頭 -- ContentType

set Headers 的方法有很多, 上面是其一, 你還可以按如下方式做:

given().header("Content-Type", "application/json")
given().headers("Accept", "application/json", "Content-Type"