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
MockitoJUnitRunner
. Untuk informasi lebih lanjut tentang perbedaan, lihat pertanyaan di stackoverflow.com/questions/10806345/… dan jawaban saya untuk itu.Collaborator collab = mock(Collaborator.class)
, menurut saya cara ini tentunya merupakan pendekatan yang valid. Meskipun ini mungkin cenderung bertele-tele, Anda dapat memperoleh pemahaman dan refactorabilitas pengujian. Kedua cara memiliki pro dan kontra, saya belum memutuskan pendekatan mana yang lebih baik. Amyway selalu mungkin untuk menulis omong kosong, dan mungkin tergantung pada konteks dan pembuat kode.Sekarang ada (mulai v1.10.7) cara keempat untuk membuat contoh tiruan, yang menggunakan aturan JUnit4 yang disebut MockitoRule .
JUnit mencari subclass TestRule yang dianotasi dengan @Rule , dan menggunakannya untuk menggabungkan Pernyataan pengujian yang disediakan Runner . Hasilnya adalah Anda dapat mengekstrak metode @Before, metode @After, dan bahkan mencoba ... menangkap pembungkus ke dalam aturan. Anda bahkan dapat berinteraksi dengan ini dari dalam pengujian Anda, seperti yang dilakukan ExpectedException .
MockitoRule berperilaku hampir persis seperti MockitoJUnitRunner , kecuali bahwa Anda dapat menggunakan pelari lain, seperti Parameterized (yang memungkinkan konstruktor pengujian Anda mengambil argumen sehingga pengujian Anda dapat dijalankan beberapa kali), atau runner pengujian Robolectric (sehingga classloadernya dapat menyediakan pengganti Java untuk kelas asli Android). Ini membuatnya lebih fleksibel untuk digunakan di versi JUnit dan Mockito terbaru.
Singkatnya:
Mockito.mock()
: Doa langsung tanpa dukungan anotasi atau validasi penggunaan.MockitoAnnotations.initMocks(this)
: Dukungan anotasi, tidak ada validasi penggunaan.MockitoJUnitRunner
: Dukungan anotasi dan validasi penggunaan, tetapi Anda harus menggunakan runner itu.MockitoRule
: Dukungan anotasi dan validasi penggunaan dengan pelari JUnit apa pun.Lihat juga: Bagaimana JUnit @Rule bekerja?
sumber
@get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule()
Ada cara yang rapi untuk melakukan ini.
Jika ini adalah Tes Unit, Anda dapat melakukan ini:
EDIT: Jika ini adalah tes Integrasi, Anda dapat melakukan ini (tidak dimaksudkan untuk digunakan seperti itu dengan Spring. Tunjukkan saja bahwa Anda dapat menginisialisasi tiruan dengan Pelari yang berbeda):
sumber
MockitoAnnotation & runner telah dibahas dengan baik di atas, jadi saya akan memberikan tuppence saya untuk yang tidak dicintai:
Saya menggunakan ini karena menurut saya ini sedikit lebih deskriptif dan saya lebih suka pengujian unit (bukan larangan yang benar) untuk tidak menggunakan variabel anggota karena saya suka pengujian saya (sebanyak mungkin) mandiri.
sumber
Sebuah contoh kecil untuk JUnit 5 Jupiter, "RunWith" telah dihapus, Anda sekarang harus menggunakan Ekstensi menggunakan Anotasi "@ExtendWith".
sumber
Jawaban lainnya bagus dan berisi lebih banyak detail jika Anda menginginkan / membutuhkannya.
Selain itu, saya ingin menambahkan TL; DR:
@RunWith(MockitoJUnitRunner.class)
@Rule public MockitoRule rule = MockitoJUnit.rule();
@Before public void initMocks() { MockitoAnnotations.initMocks(this); }
X x = mock(X.class)
(1) dan (2) dan (3) saling eksklusif.
(4) dapat digunakan dalam kombinasi dengan yang lain.
sumber