Как вводить несколько макетов одного и того же интерфейса
Класс Java (называемый ServiceCaller
), который я хочу проверить, имеет следующее:
@Autowired @Qualifier(value="serviceA")
SomeService serviceA;
@Autowired @Qualifier(value="serviceB")
SomeService serviceB;
(есть метод doWork()
, который проверяет условие и вызывает либо A, либо B).
Как я вставляю макет каждой службы в соответствующую переменную?
У моего Junit есть следующее:
@InjectMocks ServiceCaller classUnderTest = new ServiceCaller();
@Mock SomeService mockServiceA;
@Mock SomeService mockServiceB;
Тем не менее, когда я запускаю свои тесты, чтобы проверить, что служба A/B вызвана в правильном состоянии, я получаю нулевые указатели, поскольку макет не был введен.
Очевидно, что это связано с несколькими зависимостями от одного и того же интерфейса (SomeService
). Есть ли способ указать квалификатор при объявлении mock-сервиса? Или мне нужно иметь сеттеры для зависимостей и ставить старомодный способ?
Ответы
Ответ 1
Этого должно быть достаточно, чтобы назвать ваш mocks serviceA и serviceB. Из документации Mockito:
Ввод инсталляции устройства; mocks сначала будет разрешен по типу, тогда, если есть несколько свойств одного и того же типа, по совпадению имени свойства и имени макета.
В вашем примере:
@InjectMocks ServiceCaller classUnderTest;
@Mock SomeService serviceA;
@Mock SomeService serviceB;
Обратите внимание, что нет необходимости вручную создавать экземпляр класса при использовании @InjectMocks.
Тем не менее, я лично предпочитаю впрыскивать зависимости, используя конструктор. Это облегчает @InjectMocks
в тестах (просто вызовите конструктор с вашими издевательствами - без инструментов отражений или @InjectMocks
(что полезно, но скрывает некоторые аспекты)). Кроме того, с использованием TDD ясно видно, какие зависимости необходимы для тестируемого класса, а также IDE может генерировать ваши заглушки конструктора.
Spring Framework полностью поддерживает инъекцию конструктора:
@Bean
public class ServiceCaller {
private final SomeService serviceA;
private final SomeService serviceB;
@Autowired
public ServiceCaller(@Qualifier("serviceA") SomeService serviceA,
@Qualifier("serviceB") SomeService serviceB) { ... }
...
}
Этот код можно протестировать с помощью:
@Mock SomeService serviceA;
@Mock SomeService serviceB;
//in a setup or test method
ServiceCaller classUnderTest = new ServiceCaller(serviceA, serviceB);
Ответ 2
вы можете использовать свойство name, чтобы определить свой экземпляр следующим образом:
@Mock(name="serviceA") SomeService serviceA;
@Mock(name="serviceB") SomeService serviceB;
Ответ 3
При наличии однотипных зависимостей mockito прекращает вводить зависимости из-за свойств тех же типов. решить эту проблему со ссылкой на @osiris256 следующим образом.
Class ServiceLayer{
@Autowired
@Qualifier("bean1")
private InterfaceA typeA;
@Autowired
@Qualifier("bean2")
private InterfaceA typeb;
}
your test class should be
@RunWith(SpringRunner.class)
class ServiceLayerTest{
@Mock(name = "typeA")
private InterfaceA typeA;
@Mock(name = "typeB")
private InterfaceA typeB;
@InjectMocks
ServiceLayer serviceLayer;
@Before
public void initialiseBeforeTest(){
MockitoAnnotations.initMocks(this);
}
// here goes your test
@Test
public void test(){
// use your when then .....
}
}
Примечание: если вы используете SpringRunner и используете @MockBean, это не сработает, вы должны заменить @Mock (name = "") со ссылкой на @osiris256.