Đối với khởi tạo mocks , sử dụng runner hoặc các MockitoAnnotations.initMocks
giải pháp tương đương. Từ javadoc của 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.
Giải pháp đầu tiên (với MockitoAnnotations.initMocks
) có thể được sử dụng khi bạn đã định cấu hình một trình chạy cụ thể ( SpringJUnit4ClassRunner
ví dụ) trên trường hợp thử nghiệm của bạn.
Giải pháp thứ hai (với MockitoJUnitRunner
) là giải pháp cổ điển hơn và yêu thích của tôi. Mã đơn giản hơn. Sử dụng một trình chạy mang lại lợi thế lớn là xác thực tự động việc sử dụng khung (được mô tả bởi @David Wallace trong câu trả lời này ).
Cả hai giải pháp đều cho phép chia sẻ các mô phỏng (và gián điệp) giữa các phương pháp thử nghiệm. Cùng với @InjectMocks
, chúng cho phép viết các bài kiểm tra đơn vị rất nhanh chóng. Mã chế giễu chương trình được giảm bớt, các bài kiểm tra dễ đọc hơn. Ví dụ:
@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));
}
}
Ưu điểm: Mã tối thiểu
Nhược điểm: Ma thuật đen. IMO chủ yếu là do chú thích @InjectMocks. Với chú thích này "bạn đã giải phóng nỗi đau của mã" (xem các nhận xét tuyệt vời của @Brice )
Giải pháp thứ ba là tạo mô hình thử nghiệm của bạn theo từng phương pháp kiểm tra. Nó cho phép như được giải thích bởi @mlk trong câu trả lời của nó để có " kiểm tra độc lập ".
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));
}
}
Ưu điểm: Bạn chứng minh rõ ràng cách api của bạn hoạt động (BDD ...)
Nhược điểm: có nhiều mã viết sẵn hơn. (Sáng tạo chế giễu)
Lời khuyên của tôi là một sự thỏa hiệp. Sử dụng @Mock
chú thích với @RunWith(MockitoJUnitRunner.class)
, nhưng không sử dụng @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));
}
}
Ưu điểm: Bạn chứng minh rõ ràng cách api của bạn hoạt động (Cách ArticleManager
khởi tạo của tôi ). Không có mã ghi sẵn.
Nhược điểm: Thử nghiệm không khép kín, ít mã