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 .