Untuk inisialisasi tiruan , menggunakan pelari atau solusi MockitoAnnotations.initMocks
yang benar-benar setara. Dari javadoc dari 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.
Solusi pertama (dengan MockitoAnnotations.initMocks
) dapat digunakan ketika Anda telah mengkonfigurasi pelari tertentu (SpringJUnit4ClassRunner
misalnya) pada kasus pengujian Anda.
Solusi kedua (dengan MockitoJUnitRunner
) adalah yang lebih klasik dan favorit saya. Kodenya lebih sederhana. Menggunakan runner memberikan keuntungan besar dari validasi otomatis penggunaan framework (dijelaskan oleh @David Wallace dalam jawaban ini ).
Kedua solusi memungkinkan untuk berbagi tiruan (dan mata-mata) di antara metode pengujian. Ditambah dengan @InjectMocks
, mereka memungkinkan untuk menulis tes unit dengan sangat cepat. Kode tiruan boilerplate dikurangi, pengujian lebih mudah dibaca. Sebagai contoh:
@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));
}
}
Kelebihan: Kodenya minimal
Kekurangan: Ilmu hitam. IMO ini terutama karena anotasi @InjectMocks. Dengan anotasi ini "Anda kehilangan rasa sakit kode" (lihat komentar bagus @Brice )
Solusi ketiga adalah membuat tiruan Anda pada setiap metode pengujian. Ini memungkinkan seperti yang dijelaskan oleh @mlk dalam jawabannya untuk memiliki " tes mandiri ".
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));
}
}
Pro: Anda dengan jelas menunjukkan cara kerja api Anda (BDD ...)
Kekurangan: ada lebih banyak kode boilerplate. (Penciptaan mengejek)
Rekomendasi saya adalah kompromi. Gunakan @Mock
anotasi dengan @RunWith(MockitoJUnitRunner.class)
, tapi jangan gunakan @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));
}
}
Kelebihan: Anda dengan jelas menunjukkan bagaimana api Anda bekerja (How my ArticleManager
is instantiated). Tidak ada kode boilerplate.
Cons: Tes ini tidak mandiri, lebih sedikit kesulitan kode