Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Automated testing Web API

Victor
April 26, 2016

Automated testing Web API

Victor

April 26, 2016
Tweet

More Decks by Victor

Other Decks in Programming

Transcript

  1. API API определяет функциональность *при этом API не раскрывает, как

    именно эта функциональность реализована Програмный интерфейс приложения
  2. APP APP API HTTP response HTTP request API в качестве

    транспорта использует протокол HTTP
  3. APP APP API HTTP response HTTP request JSON/XML Набор определенных

    HTTP запросов, а также определение структуры HTTP-ответов
  4. Задача • Проверить статус код и возвращаемое значение curl -v

    http://weather.lanwen.ru/api/weather?city=Moscow > GET /api/weather?city=Moscow HTTP/1.1 > User-Agent: curl/7.19.7 > Host: weather.lanwen.ru > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 19 Apr 2016 16:27:33 GMT < Content-Type: application/json < Content-Length: 293 < Server: Jetty(9.2.1.v20140609) < {"city":"Moscow","daypart":"DAY","weathercode":800, "sunset":1461084341,"sunrise":1461031950,"dt":1461080976, "humidity":57,"wind":5.0, "temperatures":[{"unit":"°C","value":9.0}, {"unit":"°K","value":292.89}, {"unit":"°F","value":49.53200000000002}, {"unit":"°Kaif","value":20.0}]}
  5. Задача • Проверить статус код и возвращаемое значение curl -v

    http://weather.lanwen.ru/api/weather?city=Moscow > GET /api/weather?city=Moscow HTTP/1.1 > User-Agent: curl/7.19.7 > Host: weather.lanwen.ru > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 19 Apr 2016 16:27:33 GMT < Content-Type: application/json < Content-Length: 293 < Server: Jetty(9.2.1.v20140609) < {"city":"Moscow","daypart":"DAY","weathercode":800, "sunset":1461084341,"sunrise":1461031950,"dt":1461080976, "humidity":57,"wind":5.0, "temperatures":[{"unit":"°C","value":9.0}, {"unit":"°K","value":292.89}, {"unit":"°F","value":49.53200000000002}, {"unit":"°Kaif","value":20.0}]}
  6. Структура протокола Стартовая строка (Request Line) Заголовки (Message Headers) •

    Основные заголовки • Заголовки запроса • Заголовки ответа • Заголовки сущности • Метод URI HTTP/Версия • HTTP/Версия код пояснение
  7. Задача • Проверить статус код и возвращаемое значение curl -v

    http://weather.lanwen.ru/api/weather?city=Moscow > GET /api/weather?city=Moscow HTTP/1.1 > User-Agent: curl/7.19.7 > Host: weather.lanwen.ru > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 19 Apr 2016 16:27:33 GMT < Content-Type: application/json < Content-Length: 293 < Server: Jetty(9.2.1.v20140609) < {"city":"Moscow","daypart":"DAY","weathercode":800, "sunset":1461084341,"sunrise":1461031950,"dt":1461080976, "humidity":57,"wind":5.0, "temperatures":[{"unit":"°C","value":9.0}, {"unit":"°K","value":292.89}, {"unit":"°F","value":49.53200000000002}, {"unit":"°Kaif","value":20.0}]}
  8. Структура протокола Стартовая строка (Request Line) Заголовки (Message Headers) Тело

    сообщения или тело объекта (Entity Body) • Основные заголовки • Заголовки запроса • Заголовки ответа • Заголовки сущности message-body = entity-body | <entity-body закодировано согласно Transfer-Encoding> • Метод URI HTTP/Версия • HTTP/Версия код пояснение
  9. Задача • Проверить статус код и возвращаемое значение curl -v

    http://weather.lanwen.ru/api/weather?city=Moscow > GET /api/weather?city=Moscow HTTP/1.1 > User-Agent: curl/7.19.7 > Host: weather.lanwen.ru > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 19 Apr 2016 16:27:33 GMT < Content-Type: application/json < Content-Length: 293 < Server: Jetty(9.2.1.v20140609) < {"city":"Moscow","daypart":"DAY","weathercode":800, "sunset":1461084341,"sunrise":1461031950,"dt":1461080976, "humidity":57,"wind":5.0, "temperatures":[{"unit":"°C","value":9.0}, {"unit":"°K","value":292.89}, {"unit":"°F","value":49.53200000000002}, {"unit":"°Kaif","value":20.0}]}
  10. Плюсы автотестов на API: + Легко поддерживать + Работают с

    непреобразованными данными + Быстрые и стабильные Автотесты на API нужны для того, чтобы проверять функциональность!
  11. Задача • Проверить статус код и возвращаемое значение curl -v

    http://weather.lanwen.ru/api/weather?city=Moscow > GET /api/weather?city=Moscow HTTP/1.1 > User-Agent: curl/7.19.7 > Host: weather.lanwen.ru > Accept: */* > < HTTP/1.1 200 OK < Date: Tue, 19 Apr 2016 16:27:33 GMT < Content-Type: application/json < Content-Length: 293 < Server: Jetty(9.2.1.v20140609) < {"city":"Moscow","daypart":"DAY","weathercode":800, "sunset":1461084341,"sunrise":1461031950,"dt":1461080976, "humidity":57,"wind":5.0, "temperatures":[{"unit":"°C","value":9.0}, {"unit":"°K","value":292.89}, {"unit":"°F","value":49.53200000000002}, {"unit":"°Kaif","value":20.0}]}
  12. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  13. @Test public void weatherTest() throws IOException, JSONException { //создаем клиент

    по умолчанию HttpClient hc = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  14. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); //формируем status line запроса String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  15. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); //выполняем запрос Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  16. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); //получаем ответ HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  17. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); //достаем статус код из ответа и проверяем его int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  18. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); //получаем тело сообщения String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  19. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); //приводим тело сообщения к JSON и проверяем поле city JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  20. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); /* получаем JSON массив температур и проверяем количество и первый элемент */ JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  21. Минусы HTTP client* - Приходится писать очень много кода -

    Много поддержки * тем не менее используется очень часто
  22. https://github.com/jayway/rest-assured • предназначен для тестирования REST сервисов • синтаксис groovy/ruby

    + куча сахара • внутри HTTP клиент • json-path, xml-path REST-assured Jayway
  23. @Test public void simpleTest() throws Exception { given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow")

    .get("weather") .then().assertThat().statusCode(HttpStatus.SC_OK) .and().body("city", equalTo("Moscow")) .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  24. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  25. @Test public void simpleTest() throws Exception { //устанавливаем спецификацию по

    умолчанию, можно when() given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow") .get("weather") .then().assertThat().statusCode(HttpStatus.SC_OK) .and().body("city", equalTo("Moscow")) .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  26. @Test public void simpleTest() throws Exception { //формируем status line

    запроса через базовый uri given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow") //здесь выполненияем запрос и получаем ответ .get("weather") .then().assertThat().statusCode(HttpStatus.SC_OK) .and().body("city", equalTo("Moscow")) .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  27. @Test public void simpleTest() throws Exception { given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow")

    .get("weather") //получаем ответ для валидации .then().assertThat().statusCode(HttpStatus.SC_OK) .and().body("city", equalTo("Moscow")) .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  28. @Test public void simpleTest() throws Exception { given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow")

    .get("weather") //проверяем статус код. Здесь assertThat() - сахар .then().assertThat().statusCode(HttpStatus.SC_OK) .and().body("city", equalTo("Moscow")) .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  29. @Test public void simpleTest() throws Exception { given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow")

    .get("weather") .then().assertThat().statusCode(HttpStatus.SC_OK) //используя JSON-path проверяем тело сообщения .and().body("city", equalTo("Moscow")) //and() - сахар .and().body("temperatures", hasSize(4)) .and().body("temperatures[0].value", equalTo(9.0f)); }
  30. public interface MyService { @GET("/api/weather") Call<WeatherResp> weather(@Query("city") String city); }

    public class WeatherResp { public String city; public String getCity() { return city; } public List<Temperature> temperatures; public List<Temperature> getTemperatures() { return temperatures; } } Интерфейс API Класс ответа
  31. public class Temperature { public float value; public String getValue()

    { return value; } public String unit; public String getUnit() { return unit; } } Вспомогательный класс ответа
  32. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  33. @Test public void weatherTest() throws IOException, JSONException { HttpClient hc

    = HttpClientBuilder.create().build(); String uri = "http://weather.lanwen.ru/api/weather?city=Moscow"; Request request = Request.Get(uri); Response response = Executor.newInstance(hc).execute(request); HttpResponse httpResponse = response.returnResponse(); int statusCode = httpResponse.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); String entity = EntityUtils.toString(httpResponse.getEntity()); JSONObject resp = new JSONObject(entity); assertThat(resp.get("city"), equalTo("Moscow")); JSONArray temperatures = resp.getJSONArray("temperatures"); assertThat(temperatures.length(), equalTo(4)); assertThat(temperatures.getJSONObject(0).get("value"), equalTo(9.0)); }
  34. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { //создаем объект ретрофита Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  35. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() //указываем базовый url .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  36. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") //добавляем конвертер (отдельная зависимость) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  37. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); //генерируем API клиент по интерфейсу MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  38. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); //получаем запрос с параметром Moscow Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  39. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); //выполняем запрос, получаем объект ответа Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  40. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); //проверяем статус код assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  41. @Test public void pingShouldSeePong() throws IOException { HttpLoggingInterceptor logging =

    new HttpLoggingInterceptor(); logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); Retrofit restAdapter = new Retrofit.Builder() .baseUrl("http://localhost") .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = restAdapter.create(MyService.class); Call<PingResp> req = service.ping(); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); assertThat(resp.body().getPong(), equalTo("")); } @Test public void simpleTest() throws IOException { Retrofit retrofit = new Retrofit.Builder() .baseUrl("http://weather.lanwen.ru") .addConverterFactory(GsonConverterFactory.create()) .build(); MyService service = retrofit.create(MyService.class); Call<PingResp> req = service.weather("Moscow"); Response <PingResp> resp = req.execute(); assertThat(resp.code(), is(HttpStatus.SC_OK)); //проверяем тело ответа assertThat(resp.body().getCity(), equalTo("Moscow")); assertThat(resp.body().getTemperatures(), hasSize(4)); assertThat(resp.body().getTemperatures().get(0).getValue(), equalTo(9.0)); }
  42. Отлично подходит для модифицирующих операций • Небольшой ответ от API

    • Понятно что произошло *появился/удалился/изменился какой-то объект { "result" : "ok", "error" : "" }
  43. … и еще 200 строк Протестируй это! [{ "_id": 498817,

    "name": "Saint Petersburg", "country": "RU", "coord": { "lon": 30.264168, "lat": 59.894444 } }, { "_id": 524901, "name": "Moscow", "country": "RU", "coord": { "lon": 37.615555, "lat": 55.75222 } }, { "_id": 472757, "name": "Volgograd", "country": "RU", "coord": { "lon": 44.501839, "lat": 48.719391 } }, curl http://weather.lanwen.ru/api/cities
  44. [{ "_id": 498817, 498818 "name": "Saint Petersburg", "country": "RU", "coord":

    { "lon": 30.264168, "lat": 59.894444 } }, { "_id": 524901, "name": "Moscow", "Москва" "country": "RU", "coord": { "lon": 37.615555, "lat": 55.75222 } }] Версия 1 Отлично подходит для НЕмодифицирующих операций Версия 2 Различия [{ "_id": 498817, "name": "Saint Petersburg", "country": "RU", "coord": { "lon": 30.264168, "lat": 59.894444 } }, { "_id": 524901, "name": "Moscow", "country": "RU", "coord": { "lon": 37.615555, "lat": 55.75222 } }] [{ "_id": 498818, "name": "Saint Petersburg", "country": "RU", "coord": { "lon": 30.264168, "lat": 59.894444 } }, { "_id": 524901, "name": "Москва", "country": "RU", "coord": { "lon": 37.615555, "lat": 55.75222 } }]
  45. + Не поддерживаем актуальную спецификацию - Поддержка состояния - Нет

    гибкости - Ограниченность проверок - Ручной отсев падающих кейсов
  46. Сервис 4 Еще какой-то сервис ? Сервис 3 Сервис 5

    Сервис 2 Тестируемый сервис Сервис 1 API ... Реальность
  47. Еще какой-то фейковый сервис ? Фейковый сервис 3 Фейковый сервис

    5 Фейковый сервис 2 Тестируемый сервис Фейковый сервис 1 API ...
  48. @Rule public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT); @Test public void

    shouldSend3Callbacks() throws Exception { stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); // --------------------------------------------------- // Здесь та магия, которая инициирует общение сервисов // --------------------------------------------------- verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*"))); }
  49. // Создаем рулу для запуска и остановки пустого сервиса @Rule

    public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT); @Test public void shouldSend3Callbacks() throws Exception { stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); // --------------------------------------------------- // Здесь та магия, которая инициирует общение сервисов // --------------------------------------------------- verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*"))); }
  50. @Rule public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT); @Test public void

    shouldSend3Callbacks() throws Exception { // Настраиваем фейк-сервис принимать любые сообщения stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); // --------------------------------------------------- // Здесь та магия, которая инициирует общение сервисов // --------------------------------------------------- verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*"))); }
  51. @Rule public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT); @Test public void

    shouldSend3Callbacks() throws Exception { stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); // --------------------------------------------------- // Здесь та магия, которая инициирует общение сервисов // --------------------------------------------------- verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*"))); }
  52. @Rule public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT); @Test public void

    shouldSend3Callbacks() throws Exception { stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); // --------------------------------------------------- // Здесь та магия, которая инициирует общение сервисов // --------------------------------------------------- // Обращаясь к логу фейк-сервиса, убеждаемся в наличии нужных сообщений verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*"))); }
  53. + Ограничиваем спецификацию + Может помочь в других подходах +

    Воспроизводим любое поведение сторонних сервисов - Довольно сложен в применении - Требуются знания о внутренней логики сервиса
  54. @Step("Get request with params param={0}" + "&param2={1}&param3={2}") public void getSomeRequest(String

    param, int param1, boolean param3) { //тут код запроса } @Step @Step
  55. //Логирование в retrofit (отдельная зависимость) HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

    logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); /*--------------------------------------------------*/ //Логирование в rest-assured given().log().all(). и т.д. Логирование
  56. @Test public void badTest() { Thread.sleep(5000); //то чего ждем }

    /* Особо запущенный случай */ @Test public void veryBadTest() { Thread.sleep(10000); //то чего ждем Thread.sleep(20000); //какая-то проверка логики Thread.sleep(30000); //то чего ждем повторно Thread.sleep(40000); //еще какая-то проверка логики Thread.sleep(50000); } Не использовать Thread.sleep в тестах
  57. @Test public void goodTest() { int timeout = 10000; int

    interval = 1000; /* Каждую секунду делаем проверку до исчении таймаута 10 секунд */ assertThat(hc, withWaitFor(shoudBeBlah(), timeout, interval)); } Используйте матчер декоратор withWaitFor (htmlelements)
  58. /* Еще сойдет */ public class Test extends BaseTest {

    } /* Интересно, а что будет дальше? */ public class BaseTest extends BaseBaseTest { } /* Эм, а что было вначале? */ public abstract class BaseBaseTest extends BaseBaseBaseTest { } И так далее... Старайтесь не использовать наследования • в тестовых классах
  59. @Test public void badTest() { //тут основная логика теста String

    content = response.returnContent() .asString(); //и вдруг! какая-то бешенная регулярка String regExp = ""^\\{.*\"param\": " + "\"[a-z0-9_-]{2,13}\", " + "\"param1\": " + "\"[0-9]{3,15}\"," + "blah blah blah*\\}"; Pattern p = Pattern.compile(regExp); Matcher m = p.matcher(content); assertThat(m.matches(), equalTo(true)); } Никогда не работайте со строками!
  60. { "param": "blah" } @Generated("org.jsonschema2pojo") @JsonPropertyOrder({"param"}) public class Example {

    @JsonProperty("param") private String param; //getters and setters @JsonProperty("param") public String getParam() { return param; } @JsonProperty("param") public void setParam(String param) { this.param = param; } } JSON OBJECT
  61. public class Resp { private String param1; private int param2;

    //getters and setters... } @Test public void goodTest() { //тут основная логика теста... String content = response.returnContent() .asString(); //получаем объект, с которым удобно работать ObjectMapper mapper = new ObjectMapper(); WeatherResp obj = mapper.readValue(entity, Resp.class); //работаем с этим объектом assertThat(obj.getParam(), equalTo("blah")); assertThat(obj.getParam1(), equalTo(1)); } Работайте с объектами!
  62. //retrofit (из коробки) public interface MyService { @GET("/api/weather") Call<WeatherResp> weather(@Query("city")

    String city); }; /*--------------------------------------------------*/ // rest-assured given().baseUri("http://weather.lanwen.ru") .basePath("api").param("city", "Moscow").get("weather") .as(WeatherResp.class). и т.д. Преобразование к объекту