Подготовка макетов объектов - MockIto



есть много способов инициализировать макет объекта с помощью MockIto.
Что является лучшим способом среди них ?



1.



 public class SampleBaseTestCase {

@Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}


2.



@RunWith(MockitoJUnitRunner.class)


[редактирование]
3.



mock(XXX.class);


предложите мне, если есть какие-либо другие способы лучше, чем эти...

752   4  

4 ответов:

для инициализации глумится, используя бегунок или MockitoAnnotations.initMocks являются строго эквивалентными решениями. От javadoc из MockitoJUnitRunner:

JUnit 4.5 runner initializes mocks annotated with Mock, so that explicit usage of MockitoAnnotations.initMocks(Object) is not necessary. Mocks are initialized before each test method.


первое решение (с MockitoAnnotations.initMocks) может использоваться, когда вы уже настроили определенный бегун (SpringJUnit4ClassRunner например) на вашем тесте.

второе решение (с MockitoJUnitRunner) является более классическим и моим любимый. Код проще. Использование бегуна обеспечивает большое преимущество автоматическая проверка использования фреймворка (описано @David Wallace на ответ).

оба решения позволяют разделить насмешки (и шпионов) между методами тестирования. В сочетании с @InjectMocks, они позволяют писать модульные тесты очень быстро. Шаблонный насмешливый код сокращен, тесты легче читать. Для пример:

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock(name = "database") private ArticleDatabase dbMock;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @InjectMocks private ArticleManager manager;

    @Test public void shouldDoSomething() {
        manager.initiateArticle();
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        manager.finishArticle();
        verify(database).removeListener(any(ArticleListener.class));
    }
}

плюсы: - код-это минимальный

минусы: черная магия. ИМО это в основном связано с аннотацией @InjectMocks. С этой аннотацией "вы теряете боль кода" (см большие комментарии @Brice)


третье решение-создать свой макет для каждого метода тестирования. Это позволяет, как объяснил @mlk в ответ на "автономный тест".

public class ArticleManagerTest {

    @Test public void shouldDoSomething() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleCalculator calculator = mock(ArticleCalculator.class);
        ArticleDatabase database = mock(ArticleDatabase.class);
        UserProvider userProvider = spy(new ConsumerUserProvider());
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then
        verify(database).removeListener(any(ArticleListener.class));
    }
}

плюсы: вы четко демонстрируете, как работает ваш api (BDD...)

минусы: есть более шаблонный код. (Издевается над творением)


мой рекомендации-это компромисс. Используйте @Mock аннотация с @RunWith(MockitoJUnitRunner.class), но не используйте @InjectMocks:

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest {

    @Mock private ArticleCalculator calculator;
    @Mock private ArticleDatabase database;
    @Spy private UserProvider userProvider = new ConsumerUserProvider();

    @Test public void shouldDoSomething() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.initiateArticle();

        // then 
        verify(database).addListener(any(ArticleListener.class));
    }

    @Test public void shouldDoSomethingElse() {
        // given
        ArticleManager manager = new ArticleManager(calculator, 
                                                    userProvider, 
                                                    database);

        // when 
        manager.finishArticle();

        // then 
        verify(database).removeListener(any(ArticleListener.class));
    }
}

плюсы: вы наглядно демонстрируете, как работает ваш api (как мой ArticleManager инстанцируется). Нет шаблона код.

минусы: тест не является самодостаточным, меньше боли кода

теперь есть (по состоянию на v1.10.7) четвертый способ создания экземпляров mocks, который использует JUnit4 правила под названием MockitoRule.

@RunWith(JUnit4.class)   // or a different runner of your choice
public class YourTest
  @Rule public MockitoRule rule = MockitoJUnit.rule();
  @Mock public YourMock yourMock;

  @Test public void yourTestMethod() { /* ... */ }
}

JUnit ищет подклассы TestRule аннотируется @правилу, и использует их в оберните тестовые операторы, которые предоставляет Бегун. В результате этого вы можете извлечь @Before методы, @After методы и даже попробовать...ловите обертки в правила. Вы даже можете взаимодействовать с ними из вашего теста, таким образом, что ExpectedException делает.

MockitoRule ведет себя почти так же, как MockitoJUnitRunner, за исключением того, что вы можете использовать любой другой участник, например параметризованные (что позволяет вашим конструкторам тестов принимать аргументы, чтобы ваши тесты можно было запускать несколько раз), или Robolectric Test runner (поэтому его загрузчик классов может предоставлять замены Java для собственных классов Android). Это делает его строго более гибким к используйте в последних версиях JUnit и Mockito.

в итоге:

  • Mockito.mock(): прямой вызов без поддержки аннотаций или проверки использования.
  • MockitoAnnotations.initMocks(this): поддержка аннотаций, без использования проверки.
  • MockitoJUnitRunner: поддержка аннотаций и проверка использования, но вы должны использовать это бегун.
  • MockitoRule: поддержка аннотаций и проверка использования с любым JUnit runner.

Читайте также: Как JUnit @Правило работает?

есть аккуратный способ сделать это.

  • Если это модульный тест, вы можете сделать это:

    @RunWith(MockitoJUnitRunner.class)
    public class MyUnitTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Test
        public void testSomething() {
        }
    }
    
  • EDIT: если это интеграционный тест, вы можете сделать это(не предназначено для использования таким образом с Spring. Просто продемонстрируйте, что вы можете инициализировать насмешки с разными бегунами):

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("aplicationContext.xml")
    public class MyIntegrationTest {
    
        @Mock
        private MyFirstMock myFirstMock;
    
        @Mock
        private MySecondMock mySecondMock;
    
        @Spy
        private MySpiedClass mySpiedClass = new MySpiedClass();
    
        // It's gonna inject the 2 mocks and the spied object per reflection to this object
        // The java doc of @InjectMocks explains it really well how and when it does the injection
        @InjectMocks
        private MyClassToTest myClassToTest;
    
        @Before
        public void setUp() throws Exception {
              MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testSomething() {
        }
    }
    

MockitoAnnotations & The runner были хорошо обсуждены выше, поэтому я собираюсь бросить свой tuppence для нелюбимого:

XXX mockedXxx = mock(XXX.class);

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

Comments

    Ничего не найдено.