Share
Embed
Transcript
123 slides
-
Зачем это нужно? Первый автотест Инструменты Подходы Советы и полезные
практики План лекции -
API API определяет функциональность *при этом API не раскрывает, как
именно эта функциональность реализована Програмный интерфейс приложения -
APP APP API HTTP response HTTP request API в качестве
транспорта использует протокол HTTP -
APP APP API HTTP response HTTP request JSON/XML Набор определенных
HTTP запросов, а также определение структуры HTTP-ответов -
Задача • Проверить статус код и возвращаемое значение 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}]} -
Структура протокола Стартовая строка (Request Line) • Метод URI HTTP/Версия
• HTTP/Версия код пояснение -
Задача • Проверить статус код и возвращаемое значение 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}]} -
Структура протокола Стартовая строка (Request Line) Заголовки (Message Headers) •
Основные заголовки • Заголовки запроса • Заголовки ответа • Заголовки сущности • Метод URI HTTP/Версия • HTTP/Версия код пояснение -
Задача • Проверить статус код и возвращаемое значение 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}]} -
Структура протокола Стартовая строка (Request Line) Заголовки (Message Headers) Тело
сообщения или тело объекта (Entity Body) • Основные заголовки • Заголовки запроса • Заголовки ответа • Заголовки сущности message-body = entity-body | <entity-body закодировано согласно Transfer-Encoding> • Метод URI HTTP/Версия • HTTP/Версия код пояснение -
Задача • Проверить статус код и возвращаемое значение 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}]} -
HTTP (Hypertext Transfer protocol) + SSL (Secure Socket Layer) HTTPS
(Hypertext Transfer Protocol Secure) = -
Почта для мобильного браузера Web интерфейс Мобильное приложение Тут! И
тут тоже! API И тут! -
Плюсы автотестов на API: + Легко поддерживать + Работают с
непреобразованными данными + Быстрые и стабильные Автотесты на API нужны для того, чтобы проверять функциональность! -
Зачем это нужно? Первый автотест Инструменты Подходы Советы и полезные
практики План лекции -
Задача • Проверить статус код и возвращаемое значение 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}]} -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
Зачем это нужно? Первый автотест Инструменты Подходы Советы и полезные
практики План лекции -
Минусы HTTP client* - Приходится писать очень много кода -
Много поддержки * тем не менее используется очень часто -
https://github.com/jayway/rest-assured • предназначен для тестирования REST сервисов • синтаксис groovy/ruby
+ куча сахара • внутри HTTP клиент • json-path, xml-path REST-assured Jayway -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
https://github.com/square/retrofit • Превращает REST API сервис в Java Interface •
Внутри OkClient Square -
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 Класс ответа -
public class Temperature { public float value; public String getValue()
{ return value; } public String unit; public String getUnit() { return unit; } } Вспомогательный класс ответа -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
@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)); } -
Минусы Retrofit - Изначально не для тестов - Мало документации
http://square.github.io/retrofit/ -
Зачем это нужно? Первый автотест Инструменты Подходы Советы и полезные
практики План лекции -
Отлично подходит для модифицирующих операций • Небольшой ответ от API
• Понятно что произошло *появился/удалился/изменился какой-то объект { "result" : "ok", "error" : "" } -
… и еще 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 -
[{ "_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 } }] -
+ Не поддерживаем актуальную спецификацию - Поддержка состояния - Нет
гибкости - Ограниченность проверок - Ручной отсев падающих кейсов -
Сервис 4 Еще какой-то сервис ? Сервис 3 Сервис 5
Сервис 2 Тестируемый сервис Сервис 1 API ... Реальность -
Еще какой-то фейковый сервис ? Фейковый сервис 3 Фейковый сервис
5 Фейковый сервис 2 Тестируемый сервис Фейковый сервис 1 API ... -
@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=.*"))); } -
// Создаем рулу для запуска и остановки пустого сервиса @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=.*"))); } -
@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=.*"))); } -
@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=.*"))); } -
@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=.*"))); } -
+ Ограничиваем спецификацию + Может помочь в других подходах +
Воспроизводим любое поведение сторонних сервисов - Довольно сложен в применении - Требуются знания о внутренней логики сервиса -
Зачем это нужно? Первый автотест Инструменты Подходы Советы и полезные
практики План лекции -
@Step("Get request with params param={0}" + "¶m2={1}¶m3={2}") public void getSomeRequest(String
param, int param1, boolean param3) { //тут код запроса } @Step @Step -
//Логирование в retrofit (отдельная зависимость) HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BASIC); OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(logging).build(); /*--------------------------------------------------*/ //Логирование в rest-assured given().log().all(). и т.д. Логирование -
@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 в тестах -
@Test public void goodTest() { int timeout = 10000; int
interval = 1000; /* Каждую секунду делаем проверку до исчении таймаута 10 секунд */ assertThat(hc, withWaitFor(shoudBeBlah(), timeout, interval)); } Используйте матчер декоратор withWaitFor (htmlelements) -
/* Еще сойдет */ public class Test extends BaseTest {
} /* Интересно, а что будет дальше? */ public class BaseTest extends BaseBaseTest { } /* Эм, а что было вначале? */ public abstract class BaseBaseTest extends BaseBaseBaseTest { } И так далее... Старайтесь не использовать наследования • в тестовых классах -
@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)); } Никогда не работайте со строками! -
{ "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 -
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)); } Работайте с объектами! -
//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). и т.д. Преобразование к объекту -
План лекции Зачем это нужно? Первый автотест Инструменты Подходы Советы
и полезные практики