6 полезных советов по использованию REST-assured
Мы уже рассказывали о преимуществах REST-assured — одной из самых популярных Java-библиотек, которая предназначена для автоматизации тестирования REST-API. Следующие советы позволят вам использовать этот инструмент более эффективно.
1. Выносите end-point'ы в отдельное место
С одной стороны, это очевидно. Но увы, до сих пор нередко встречается код, имеющий захардкоженные end-point'ы в запросе. В целом же, лучше выносить end-point'ы в статические константы финального класса. Но тут есть нюанс — избегайте антипаттерна «константный интерфейс», т. к. это является плохой практикой. И помните, что REST-assured даёт возможность выносить параметры пути, к примеру:
public final class EndPoints { public static final String users = "/users/{id}"; ... } given().pathParams("id", someId).get(EndPoints.users)...; // или так given().get(EndPoints.users, someId)....
Кроме того, если вы во многих запросах применяете одинаковый базовый путь, считается хорошей практикой выносить его в отдельную константу, передавая в basePath, как в примере ниже:
// имеем url приложения http://host:port/appname/rest/someEndpoints private static final basePath = "/appname/rest/"; .. // можно задать базовый путь на глобальном уровне, // и он будет использоваться ко всем запросам: RestAssured.basePath = basePath; // либо на уровне одного запроса: given().basePath(basePath)... // либо на уровне спецификации, но об этом поговорим позже
То же применимо как к хосту, так и к порту тестируемого программного приложения.
2. ContentType/Accept
Эти заголовки применяются почти во всех HTTP-запросах. Разработчики REST-assured прекрасно это понимают, поэтому они обеспечили возможность их установки посредством вызова специальных методов:
// вот то плохая практика написания: given().header("content-type", "application/json").header("accept", "application/json")...; // а вот эта хорошая: given().contentType(ContentType.JSON).accept(ContentType.JSON)...;
Хорошей практикой станет установка данных заголовков в спецификации либо на глобальном уровне. Результат — повышение читабельности кода.
3. StatusCode и т. п.
Для выполнения проверки каждой составляющей HTTP-ответа, библиотека REST-assured предоставляет удобный синтаксис. Но на практике часто можно увидеть следующий код:
// это плохая практика написания: Response response = given()...when().get(someEndpoint); Assert.assertEquals(200, response.then().extract().statusCode()); //а это хорошая: given()...when().get(someEndpoint).then().statusCode(200);
4. Используйте спецификации
Мы знаем, что дублировать код не есть хорошо. Для уменьшения дублирования нужно использовать спецификации. В REST-assured есть возможность создания спецификации и для запроса, и для ответа. В спецификацию запроса можно вынести всё, что можно продублировать в запросах.
RequestSpecification requestSpec = new RequestSpecBuilder() .setBaseUri("http://localhost") .setPort(8080) .setAccept(ContentType.JSON) .setContentType(ContentType.ANY) ... .log(LogDetail.ALL) .build(); // мы можем задать одну спецификацию для всех запросов: RestAssured.requestSpecification = requestSpec; // либо для отдельного: given().spec(requestSpec)...when().get(someEndpoint);
Кроме того, в спецификацию ответа можно выносить все проверки, дублируемые от запроса к запросу.
ResponseSpecification responseSpec = new ResponseSpecBuilder() .expectStatusCode(200) .expectBody(containsString("success")) .build(); // мы можем задать одну спецификацию для всех ответов: RestAssured.responseSpecification = responseSpec; // либо для отдельного: given()...when().get(someEndpoint).then().spec(responseSpec)...;
Также мы можем создавать несколько спецификаций для различных типов запросов/ответов и применять их в нужном случае.
5. Не надо писать собственные костыли для преобразования объектов
Не нужно преобразовывать свои POJO в JSON, используя Jackson ObjectMapper'а, а потом передавать полученную строку в тело запроса. Почему? Потому что с этой задачей отлично справляется библиотека REST-assured. Причём применяется всё тот же Jackson либо Gson с учётом того, что находится в classpath. А чтобы выполнить преобразование в XML, применяется JAXB. Что касается исходного формата, то он определяется автоматически по значению Content-Type.
given().contentType(ContentType.JSON).body(somePojo) .when().post(EndPoints.add) .then() .statusCode(201); // то же самое действует и в обратную сторону: SomePojo pojo = given(). .when().get(EndPoints.get) .then().extract().body().as(SomePojo.class);
Вдобавок к вышесказанному, REST-assured хорошо справляется и с преобразованием HashMap в JSON и в обратном порядке.
6. Используйте всю силу Groovy
Так как сама библиотека написана на Groovy, она даёт возможность использовать к полученному JSON/XML-ответу разные методы из Groovy. Допустим:
// методы find, findAll используются к коллекции для поиска первого и всех вхождений, а метод collect — для создания новой коллекции из найденных результатов. // переменная it создается неявно, указывая на текущий элемент коллекции Map<String, ?> map = get(EndPoints.anyendpoint).path("rootelement.find { it.title =~ 'anythingRegExp'}"); // можно явно задать название переменной, указывающей на текущий элемент Map<String, ?> map = get(EndPoints.anyendpoint).path("rootelement.findAll { element -> element.title.length() > 4 }"); // можно применять методы max, min, sum в целях суммирования всех значений коллекции и поиска максимального и минимально значений String expensiveCar = get(EndPoints.cars).path("cars.find { it.title == 'Toyota Motor Corporation'}.models.max { it.averagePrice }.title");
Применение методов из Groovy сократит количество кода, написанного вами в целях поиска необходимого значения из ответа.