Mockito, JUnit и весна
Я начал узнавать о Мокито только сегодня. Я написал несколько простых тестов (с JUnit, см. ниже), но я не могу понять, как я могу использовать макет объекта внутри Spring's manages beans. Что такое лучшие практики для работы с весны. Как я должен вводить издевательскую зависимость в мой Боб?
вы можете пропустить до вернемся к моему вопросу.
прежде всего, то, что я узнал.
Это очень хорошая статья насмешки не окурки это объясняет основы (проверки макета проверка поведения не государственную поверку). Тогда есть хороший пример здесь Mockito
Здесь легче издеваться с mockito у нас есть объяснение, что макетные объекты Mockito-это оба mock и заглушки.
здесь Mockito и здесь Matchers вы можете найти больше примеров.
этот тест
@Test
public void testReal(){
List<String> mockedList = mock(List.class);
//stubbing
//when(mockedList.get(0)).thenReturn("first");
mockedList.get(anyInt());
OngoingStubbing<String> stub= when(null);
stub.thenReturn("first");
//String res = mockedList.get(0);
//System.out.println(res);
//you can also verify using argument matcher
//verify(mockedList).get(anyInt());
verify(mockedList);
mockedList.get(anyInt());
}
работает просто отлично.
вернемся к моему вопросу. здесь впрыскивая Mockito издевается над весенним Бобом кто-то пытается использовать пружины ReflectionTestUtils.setField(), но не здесь Весенние Интеграционные Тесты, Создание Макетных Объектов у нас есть рекомендация изменить контекст весны.
Я действительно не понял последние две ссылки... Может кто-нибудь объяснить мне, какая проблема у Весны с Мокито? Что плохого это решение?
@InjectMocks
private MyTestObject testObject
@Mock
private MyDependentObject mockedObject
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
https://stackoverflow.com/a/8742745/1137529
EDIT: я не совсем понял. Я приведу 3 примера кода, чтобы прояснить себя:
Предположим, у нас есть bean HelloWorld с методом printHello() и Боб HelloFacade с методом sayHello что переадресовывает вызовы метода HelloWorld printHello().
первый пример использует контекст Spring и без пользовательского бегуна, используя ReflectionTestUtils для инъекции зависимостей (DI):
public class Hello1Test {
private ApplicationContext ctx;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}
@Test
public void testHelloFacade() {
HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
HelloWorld mock = mock(HelloWorld.class);
doNothing().when(mock).printHello();
ReflectionTestUtils.setField(obj, "hello", mock);
obj.sayHello();
verify(mock, times(1)).printHello();
}
}
как указал @Noam, есть способ запустить его без явного вызова MockitoAnnotations.initMocks(this);. Я также отброшу использование контекста весны на этом примере.
@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {
@InjectMocks
private HelloFacade obj = new HelloFacadeImpl();
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
еще один способ сделать это
public class Hello1aTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@InjectMocks
private HelloFacadeImpl obj;
@Mock
private HelloWorld mock;
@Test
public void testHelloFacade() {
doNothing().when(mock).printHello();
obj.sayHello();
}
}
нет, что в preivious примере мы должны вручную instaniate HelloFacadeImpl и назначить его HelloFacade, потому что hellofacade-это интерфейс. В последнем примере мы можем просто объявить HelloFacadeImpl, и Mokito создаст его для нас. Недостаток этого подхода в том, что теперь unit-under-test является impl-классом, а не интерфейсом.
7 ответов:
честно говоря, я не уверен, что действительно понимаю ваш вопрос: P я постараюсь прояснить как можно больше, из того, что я получаю от вашего первоначального вопроса:
во-первых, в большинстве случаев, вы не должны иметь никакого беспокойства по весне. Вам редко нужно, чтобы spring участвовал в написании вашего модульного теста. В обычном случае вам нужно только создать экземпляр тестируемой системы (SUT, цель для тестирования) в вашем модульном тесте и ввести зависимости SUT в тесте тоже. Зависимости обычно макет/заглушки.
ваш оригинальный предложенный способ и Пример 2, 3 точно делают то, что я описываю выше.
в некоторых редких случаях (например, интеграционные тесты или некоторые специальные модульные тесты) вам нужно создать контекст приложения Spring и получить SUT из контекста приложения. В таком случае, я считаю, что вы можете:
1) Создайте свой SUT в spring app ctx, получите ссылку на него и введите в него насмешки
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest { @Autowired @InjectMocks TestTarget sut; @Mock Foo mockFoo; @Before /* Initialized mocks */ public void setup() { MockitoAnnotations.initMocks(this); } @Test public void someTest() { // .... } }или
2) следовать способ описан в вашей ссылке Весенние Интеграционные Тесты, Создание Макетных Объектов. Этот подход заключается в создании насмешек в контексте приложения Spring, и вы можете получить макет объекта из приложения ctx, чтобы сделать ваш stubbing / verification:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest { @Autowired TestTarget sut; @Autowired Foo mockFoo; @Test public void someTest() { // .... } }оба способа должны работать. Основное различие заключается в том, что в первом случае будут введены зависимости после прохождения жизненного цикла spring и т. д. (например, инициализация bean), в то время как последний случай вводится перед ручками. Например, если ваш SUT реализует Spring'S InitializingBean, и процедура инициализации включает в себя зависимости, вы увидите разницу между этими двумя подходами. Я считаю, что нет правильного или неправильного для этих двух подходов, пока вы знаете, что делаете.
просто дополнение, @Mock, @Inject, MocktoJunitRunner и т. д. Все они не нужны при использовании Mockito. Они просто утилиты, чтобы сохранить вас набрав Mockito.МОК(Фу.класс) и куча вызовов сеттера.
ваш вопрос, кажется, спрашивает о том, какой из трех примеров вы дали является предпочтительным подходом.
Пример 1 использование тестов отражения не является хорошим подходом для тестирование. Вы действительно не хотите загружать контекст spring вообще для модульного теста. Просто издевайтесь и вводите то, что требуется, как показано на других примерах.
вы хотите загрузить весенний контекст, если вы хотите сделать некоторые интеграционное тестирование, однако я бы предпочел использовать
@RunWith(SpringJUnit4ClassRunner.class)для выполнения загрузки контекста вместе с@AutowiredЕсли вам нужен доступ к своему бобы явно.Пример 2 является допустимым подходом и использование
@RunWith(MockitoJUnitRunner.class)устранит необходимость указывать метод @Before и явный вызовMockitoAnnotations.initMocks(this);Пример 3 еще один правильный подход, который не использует
@RunWith(...). Вы не создали экземпляр тестируемого классаHelloFacadeImplявно, но вы могли бы сделать то же самое с примером 2.Я предлагаю использовать Пример 2 для модульного тестирования, так как это уменьшает код. Вы можете вернуться к более подробной конфигурации, если и когда вы вынуждены это сделать.
введение некоторых новых испытательных установок весной 4.2.RC1 позволяет писать весенние интеграционные тесты, которые не полагаются на
SpringJUnit4ClassRunner. Проверьте этой часть документации.в вашем случае вы можете написать свой весенний интеграционный тест и по-прежнему использовать насмешки:
@RunWith(MockitoJUnitRunner.class) @ContextConfiguration("test-app-ctx.xml") public class FooTest { @ClassRule public static final SpringClassRule SPRING_CLASS_RULE = new SpringClassRule(); @Rule public final SpringMethodRule springMethodRule = new SpringMethodRule(); @Autowired @InjectMocks TestTarget sut; @Mock Foo mockFoo; @Test public void someTest() { // .... } }
вам действительно не нужно
MockitoAnnotations.initMocks(this);Если вы используете mockito 1.9 ( или новее ) - все что вам нужно это:@InjectMocks private MyTestObject testObject; @Mock private MyDependentObject mockedObject;The
@InjectMocksаннотация будет вводить все ваши насмешки вMyTestObject"объект".
вот мое краткое резюме.
если вы хотите написать модульный тест, не используйте Spring applicationContext, потому что вы не хотите, чтобы какие-либо реальные зависимости вводились в класс, который вы тестируете. Вместо этого используйте насмешки, либо с
@RunWith(MockitoJUnitRunner.class)Аннотация В верхней части класса, или сMockitoAnnotations.initMocks(this)в методе @Before.если вы хотите написать интеграционный тест, использовать:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("yourTestApplicationContext.xml")настроить контекст базы данных в памяти для образец. Обычно вы не используете mocks в интеграционных тестах, но вы можете сделать это с помощью
MockitoAnnotations.initMocks(this)подход, описанный выше.
разница в том, должны ли вы создать экземпляр
@InjectMocksаннотированное поле находится в версии Mockito, а не в том, используете ли вы MockitoJunitRunner илиMockitoAnnotations.initMocks. В 1.9, который также будет обрабатывать какую-то инъекцию конструктора@Mockполя, он будет делать экземпляр для вас. В более ранних версиях вы должны создать его самостоятельно.вот как я делаю модульное тестирование моих весенних бобов. Нет никаких проблем. Люди впадают в замешательство, когда они хотят использовать Файлы конфигурации Spring фактически делают инъекцию mocks, которая пересекает точку модульных тестов и интеграционных тестов.
и конечно тестируемый модуль является Impl. Вам нужно проверить реальную конкретную вещь, не так ли? Даже если вы объявили его как интерфейс, вам нужно будет создать экземпляр реальной вещи, чтобы проверить ее. Теперь вы можете попасть в шпионов, которые являются обертками заглушки/макета вокруг реальных объектов, но это должно быть для угловых случаев.
если бы Вы перенесли свой проект на Spring Boot 1.4, вы могли бы использовать новую аннотацию
@MockBeanза подделываниеMyDependentObject. С помощью этой функции вы можете удалить Mockito@Mockи@InjectMocksаннотации из теста.
Comments