Разделы портала

Онлайн-тренинги

.
Модель Client-Test для тестирования REST API
10.01.2024 00:00

Автор: Куо Динг (Kwo Ding)
Оригинал статьи
Перевод: Ольга Алифанова

Разделение ответственности – отличный принцип программирования, разделяющий код на отдельные секции, у каждой из которых свои обязанности. Применим этот принцип к коду тестов, создавая тесты REST API.

Допустим, у нас есть сервис адресной книги с API для выполнения CRUD-операций (создание, чтение, обновление, удаление).

К примеру:

  • GET /contacts/{contactId} для получения данных о контактном лице.
  • POST /contacts для создания контакта.

При помощи Rest Assured можно создать контакт в тесте:

@Test
void shouldCreateContact() {
Contact contact = Contact.newBuilder()
.withLastName("Bond")
.withFirstName("James")
.withPhone("202-555-0185")
.build();
 
given()
// Логирование всех запросов и ответов
.log().all()
.baseUri("http://localhost:8080/contacts")
.contentType(ContentType.JSON)
.when()
.body(contact)
.post()
.then()
.statusCode(201);
}

Модель client-test

Довольно просто, не так ли? Так, но по мере роста количества тестов хочется избавиться от всех этих настроек, чтобы просто выполнить http-запрос теста.

Переработаем это с использованием модели client-test:

  • Клиент будет содержать все настройки для выполнения http-запросов.
  • Тест сконцентрируется на самом тесте (действия, проверки).

Такая настройка явно разделит ответственность.

Начнем с настройки базового клиента с общей конфигурацией.

public abstract class BaseClient {
 
private String baseUri;
 
public BaseClient(String baseUri) {
this.baseUri = baseUri;
}
 
public RequestSpecification requestSpec() {
RestAssuredConfig config = RestAssured.config()
.httpClient(httpClientConfig().setParam("http.connection.timeout", 10000));
 
RequestSpecification requestSpecification = RestAssured.with()
.config(config)
// Логирование всех запросов и ответов
.filter(new ResponseLoggingFilter())
.filter(new RequestLoggingFilter())
.baseUri(this.baseUri);
 
setLoggingFilters(requestSpecification);
 
return requestSpecification;
}
}

Замечу, что это упрощенный пример, куда можно добавить больше настроек – к примеру, для (де)сериализации объектов.

Теперь создадим ContactClient, который имеет дело непосредственно с http-запросами.

public class ContactClient extends BaseClient {
 
private static final String CONTACTS = "/contacts";
 
public ContactClient() {
super(BASE_URI);
}
 
public Response createContact(Contact contact) {
return requestSpec()
.contentType(JSON)
.body(contact)
.post(CONTACTS);
}
 
public Response getContact(Long contactId) {
return requestSpec()
.contentType(JSON)
.get(CONTACTS  + "/" + contactId);
}
}

ОК, вернемся к тесту. В примере ниже тест не замусорен настройками http-переговоров и концентрируется только на шагах/действиях и проверках.

private final ContactClient client = new ContactClient();
 
@Test
void shouldCreateContact() {
Contact contact = Contact.newBuilder()
.withLastName("Bond")
.withFirstName("James")
.withPhone("202-555-0185")
.build();
 
client.createContact(contact)
.then()
.statusCode(201);
}

Заключение

Разделение ответственности – прекрасный принцип программирования, который, как и любой другой хороший принцип, стоит применять к коду тестов. При внедрении тестов REST API логично, чтобы тест содержал только шаги и проверки. Выполнение http-запросов можно абстрагировать в так называемый клиент. Таким образом тесты станут четче, а клиент(ы) можно легко расширить и использовать в других тестах.

Обсудить в форуме