Как вводить макет в контексте Spring

У меня есть тест, который использует некоторые Spring контексты. В этих контекстах объявляется ряд beans. Я хочу, чтобы тест использовал фактическую реализацию beans контекстов, EXCEPT для одного из них, для которого я хочу использовать MOCK.

Я попытался сделать компонент Test a Configuration (с аннотацией @Configuration), но XML, кажется, имеет приоритет над аннотацией @ Bean, поэтому он не работает, таким образом:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"context1.xml", "context2.xml", ...})
@Configuration
public class MyTest{

    @Inject
    private MyTargetBean target;

    private AnotherBean myMock = mock(AnotherBean.class);

    @Bean
    public AnotherBean myMock() { return myMock; }

    .....

Я знаю, что могу определить Mocks в XML, но для этого мне понадобится дополнительный XML файл для каждого теста, в котором я хочу это сделать. Я хочу избежать этой сложности.

Есть ли способ вставить bean (например, макет) в контексте, помимо XML?

Спасибо!

Ответ 2

Да, вы на правильном пути, ставить макет @Bean в классе @Configuration - это один подход, и я опишу свой опыт:

Фокус в том, что вам нужно использовать другой набор XML файлов, предназначенных исключительно для тестирования, которые исключают текущие версии этих beans.

@ContextConfiguration(locations = {"context1-test.xml", "context2-test.xml", ...})

И файлы -test-xml идут в src/test/resources.

По крайней мере, это был мой опыт делать то же самое. Возможно, есть какой-то способ "переопределить" beans с макетными версиями, но пока я не знаю об этом.

Я также решил поместить мошек (у меня было 5 из них) все вместе в собственной конфигурации:

@Configuration
public class MockServicesProvider {
     @Bean
     public AnotherBean myMock() { return mock(AnotherBean.class); }
}

Еще одна интересная часть этой проблемы - обычное использование initMocks(this); в методе @Before вашего тестового класса.

Если издевательства используются в других местах (и это то, почему вы их подключаете...), то initMocks(this) удалит их между тестами (а не буквально - только новые макеты будут созданы и любые другие макеты, подключенные к другим объектам, будут "потеряны" ).

Решением этого является вызов mockito reset(mockObject) в методе @Before перед каждым тестом. Эти же издевательства reset (все when и взаимодействия), не создавая новых mocks.

Обратите внимание, что в документах Mockito для reset очень строго сказано, что этот метод не следует обычно использовать, за исключением случаев, когда mocks применяется через инъекцию зависимостей, как это мы делаем в этом случае:)

Удачи!