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

Transcript

123 slides
  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). и т.д. Преобразование к объекту

More Decks by Victor

Other Decks in Programming