153

MockItoを使用してモックオブジェクトを初期化する方法はたくさんあります。これらの中で最良の方法は何ですか?

1.1。

 public class SampleBaseTestCase {

   @Before public void initMocks() {
       MockitoAnnotations.initMocks(this);
   }
@RunWith(MockitoJUnitRunner.class)
mock(XXX.class);

これらよりも良い方法が他にあるかどうか私に提案してください...

4

8 に答える 8

176

モックの初期化では、ランナーまたはを使用することMockitoAnnotations.initMocksは厳密に同等のソリューションです。MockitoJUnitRunnerのjavadocから:

JUnit 4.5ランナーは、Mockで注釈が付けられたモックを初期化するため、MockitoAnnotations.initMocks(Object)を明示的に使用する必要はありません。モックは、各テストメソッドの前に初期化されます。


最初のソリューション(を使用)は、テストケースで特定のランナー(たとえば)をMockitoAnnotations.initMocks既に構成している場合に使用できます。SpringJUnit4ClassRunner

2番目の解決策(を使用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));
    }
}

長所:コードは最小限です

短所:黒魔術。IMOは、主に@InjectMocksアノテーションが原因です。このアノテーション を使用すると、「コードの苦痛を和らげることができます」 ( @Briceのすばらしいコメントを参照)


3番目の解決策は、各テストメソッドでモックを作成することです。@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がどのように機能するか(myがどのようにArticleManagerインスタンス化されるか)を明確に示します。ボイラープレートコードはありません。

短所:テストは自己完結型ではなく、コードの苦痛が少ない

于 2013-03-19T08:53:40.753 に答える
10

これを行うためのきちんとした方法があります。

  • 単体テストの場合、これを行うことができます:

    @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() {
        }
    }
    
  • 編集:統合テストの場合、これを行うことができます(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() {
        }
    }
    
于 2013-03-19T09:48:12.540 に答える
10

MockitoAnnotations とランナーについては上記で十分に説明したので、愛されていない人のためにタペンスを投入します。

XXX mockedXxx = mock(XXX.class);

私がこれを使用するのは、もう少し説明的であることがわかり、テストが自己完結型であることを望んでいるため、メンバー変数を使用しないことを (適切な禁止ではなく) 好むためです。

于 2013-03-19T10:06:56.690 に答える
8

JUnit 5 Jupiter の小さな例では、「RunWith」が削除されました。「@ExtendWith」アノテーションを使用して拡張機能を使用する必要があります。

@ExtendWith(MockitoExtension.class)
class FooTest {

  @InjectMocks
  ClassUnderTest test = new ClassUnderTest();

  @Spy
  SomeInject bla = new SomeInject();
}
于 2020-04-01T10:00:42.363 に答える