Перейти к основному содержимому

Издевательство над RestTemplate весной

· 3 мин. чтения

1. Введение

Мы часто сталкиваемся с приложениями, которые выполняют какой-то веб-запрос. Когда дело доходит до тестирования этого поведения, у нас есть несколько вариантов с приложениями Spring.

** В этом кратком руководстве мы рассмотрим всего пару способов имитации таких вызовов, выполняемых только через** RestTemplate .

Мы начнем с тестирования с Mockito, популярной библиотеки имитации. Затем мы будем использовать Spring Test, который предоставляет нам механизм для создания фиктивного сервера для определения взаимодействия с сервером.

2. Использование Мокито

Мы можем использовать Mockito, чтобы полностью смоделировать RestTemplate . При таком подходе тестирование нашего сервиса будет таким же простым, как и любой другой тест, связанный с насмешками .

Предположим, у нас есть простой класс EmployeeService , который получает информацию о сотрудниках через HTTP:

@Service
public class EmployeeService {

@Autowired
private RestTemplate restTemplate;

public Employee getEmployee(String id) {
ResponseEntity resp =
restTemplate.getForEntity("http://localhost:8080/employee/" + id, Employee.class);

return resp.getStatusCode() == HttpStatus.OK ? resp.getBody() : null;
}
}

Теперь давайте реализуем наш тест для предыдущего кода: ``

@ExtendWith(MockitoExtension.class)
public class EmployeeServiceTest {

@Mock
private RestTemplate restTemplate;

@InjectMocks
private EmployeeService empService = new EmployeeService();

@Test
public void givenMockingIsDoneByMockito_whenGetIsCalled_shouldReturnMockedObject() {
Employee emp = new Employee(E001, "Eric Simmons");
Mockito
.when(restTemplate.getForEntity(
“http://localhost:8080/employee/E001, Employee.class))
.thenReturn(new ResponseEntity(emp, HttpStatus.OK));

Employee employee = empService.getEmployee(id);
Assertions.assertEquals(emp, employee);
}
}

В приведенном выше тестовом классе JUnit мы сначала попросили Mockito создать фиктивный экземпляр RestTemplate , используя аннотацию @Mock .

Затем мы аннотировали экземпляр EmployeeService с помощью @InjectMocks , чтобы внедрить в него фиктивный экземпляр.

Наконец, в тестовом методе мы определили поведение нашего макета, используя поддержку Mockito when/then .

3. Использование весеннего теста

Модуль `Spring Test включает фиктивный сервер с именем MockRestServiceServer. **При таком подходе мы настраиваем сервер для возврата определенного объекта, когда определенный запрос отправляется через наш экземпляр RestTemplate . ** Кроме того, мы можем проверить()` на этом экземпляре сервера, все ли ожидания оправдались.

MockRestServiceServer на самом деле работает, перехватывая вызовы HTTP API, используя MockClientHttpRequestFactory . На основе нашей конфигурации он создает список ожидаемых запросов и соответствующих ответов. Когда экземпляр RestTemplate вызывает API, он ищет запрос в своем списке ожиданий и возвращает соответствующий ответ.

Таким образом, устраняется необходимость запуска HTTP-сервера на любом другом порту для отправки фиктивных ответов.

Давайте создадим простой тест для того же примера getEmployee() , используя MockRestServiceServer :

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = SpringTestConfig.class)
public class EmployeeServiceMockRestServiceServerUnitTest {

@Autowired
private EmployeeService empService;
@Autowired
private RestTemplate restTemplate;

private MockRestServiceServer mockServer;
private ObjectMapper mapper = new ObjectMapper();

@BeforeEach
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}

@Test
public void givenMockingIsDoneByMockRestServiceServer_whenGetIsCalled_thenReturnsMockedObject()() {
Employee emp = new Employee("E001", "Eric Simmons");
mockServer.expect(ExpectedCount.once(),
requestTo(new URI("http://localhost:8080/employee/E001")))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(mapper.writeValueAsString(emp))
);

Employee employee = empService.getEmployee(id);
mockServer.verify();
Assertions.assertEquals(emp, employee);
}
}

В предыдущем фрагменте мы использовали статические методы из MockRestRequestMatchers и MockRestResponseCreators , чтобы определить ожидание и ответ на вызов REST в ясной и удобочитаемой форме:

import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;      
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;

Мы должны помнить, что RestTemplate в тестовом классе должен быть тем же экземпляром, который используется в классе EmployeeService . Чтобы гарантировать это, мы определили bean-компонент RestTemplate в конфигурации spring и автоматически подключили экземпляр как в тесте, так и в реализации:

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

Использование MockRestServiceServer очень полезно, когда мы пишем наши интеграционные тесты, и нам нужно только имитировать внешние HTTP-вызовы.

4. Вывод

В этой краткой статье мы обсудили несколько эффективных вариантов имитации внешних вызовов REST API через HTTP при написании модульных тестов.

Исходный код вышеуказанной статьи доступен на GitHub .