Инъекция Mockito Mocks в бобы
1. обзор
В этой статье будет показано, как использовать внедрение зависимостей для вставки макетов Mockito в Spring Beans для модульного тестирования.
В реальных приложениях, где компоненты часто зависят от доступа к внешним системам, важно обеспечить надлежащую изоляцию теста, чтобы мы могли сосредоточиться на тестировании функциональности данного модуля без необходимости задействовать всю иерархию классов для каждого теста.
Инъекция макета - это чистый способ ввести такую изоляцию.
2. Maven Зависимости
Следующие зависимости Maven необходимы для модульных тестов и фиктивных объектов:
org.springframework.boot
spring-boot-starter
1.3.1.RELEASE
org.springframework.boot
spring-boot-starter-test
1.3.1.RELEASE
test
org.mockito
mockito-core
2.21.0
Мы решили использовать Spring Boot для этого примера, но классический Spring также будет работать нормально.
3. Написание теста
3.1. Бизнес-логика
Сначала мы напишем бизнес-логику, которая будет проверена:
@Service
public class NameService {
public String getUserName(String id) {
return "Real user name";
}
}
КлассNameService будет внедрен в:
@Service
public class UserService {
private NameService nameService;
@Autowired
public UserService(NameService nameService) {
this.nameService = nameService;
}
public String getUserName(String id) {
return nameService.getUserName(id);
}
}
Для этого урока указанные классы возвращают одно имя независимо от предоставленного идентификатора. Это сделано для того, чтобы мы не отвлекались на тестирование сложной логики.
Нам также понадобится стандартный основной класс Spring Boot для сканирования компонентов и инициализации приложения:
@SpringBootApplication
public class MocksApplication {
public static void main(String[] args) {
SpringApplication.run(MocksApplication.class, args);
}
}
3.2. Тесты
Теперь перейдем к логике тестирования. Прежде всего, нам нужно настроить контекст приложения для тестов:
@Profile("test")
@Configuration
public class NameServiceTestConfiguration {
@Bean
@Primary
public NameService nameService() {
return Mockito.mock(NameService.class);
}
}
Аннотация@Profile сообщает Spring применять эту конфигурацию только тогда, когда активен «тестовый» профиль. Аннотация@Primary предназначена для того, чтобы убедиться, что этот экземпляр используется вместо реального для автоматического подключения. Сам метод создает и возвращает mockito-макет нашего классаNameService.
Теперь мы можем написать модульный тест:
@ActiveProfiles("test")
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MocksApplication.class)
public class UserServiceUnitTest {
@Autowired
private UserService userService;
@Autowired
private NameService nameService;
@Test
public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
Mockito.when(nameService.getUserName("SomeId")).thenReturn("Mock user name");
String testName = userService.getUserName("SomeId");
Assert.assertEquals("Mock user name", testName);
}
}
Мы используем аннотацию@ActiveProfiles, чтобы включить «тестовый» профиль и активировать макет конфигурации, который мы написали ранее. Из-за этого Spring автоматически подключает реальный экземпляр классаUserService, но имитацию классаNameService. Сам тест является довольно типичным тестом JUnit + Mockito. Мы настраиваем желаемое поведение макета, затем вызываем метод, который хотим проверить, и утверждаем, что он возвращает ожидаемое нами значение.
Также возможно (хотя и не рекомендуется) избегать использования профилей среды в таких тестах. Для этого удалите аннотации@Profile and @ActiveProfiles и добавьте аннотацию@ContextConfiguration(classes = NameServiceTestConfiguration.class) к классуUserServiceTest.
4. Заключение
В учебном пособии показано, как вводить макеты Mockito в Spring Beans. Реализацию можно найти вexample GitHub project.