Sự khác biệt giữa @Mock
và @InjectMocks
trong khung Mockito là gì?
Sự khác biệt giữa @Mock
và @InjectMocks
trong khung Mockito là gì?
Câu trả lời:
@Mock
tạo ra một giả @InjectMocks
tạo một thể hiện của lớp và đưa các giả được tạo ra với các chú thích @Mock
(hoặc @Spy
) vào thể hiện này.
Lưu ý rằng bạn phải sử dụng @RunWith(MockitoJUnitRunner.class)
hoặc Mockito.initMocks(this)
để khởi tạo các giả này và tiêm chúng.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Đây là một mã mẫu về cách thức @Mock
và @InjectMocks
hoạt động.
Nói rằng chúng tôi có Game
và Player
lớp học.
class Game {
private Player player;
public Game(Player player) {
this.player = player;
}
public String attack() {
return "Player attack with: " + player.getWeapon();
}
}
class Player {
private String weapon;
public Player(String weapon) {
this.weapon = weapon;
}
String getWeapon() {
return weapon;
}
}
Như bạn thấy, Game
lớp cần Player
phải thực hiện một attack
.
@RunWith(MockitoJUnitRunner.class)
class GameTest {
@Mock
Player player;
@InjectMocks
Game game;
@Test
public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
assertEquals("Player attack with: Sword", game.attack());
}
}
Mockito sẽ chế nhạo một lớp Người chơi và đó là hành vi sử dụng when
và thenReturn
phương thức. Cuối cùng, sử dụng @InjectMocks
Mockito sẽ đưa nó Player
vào Game
.
Lưu ý rằng bạn thậm chí không phải tạo một new Game
đối tượng. Mockito sẽ tiêm nó cho bạn.
// you don't have to do this
Game game = new Game(player);
Chúng tôi cũng sẽ có hành vi tương tự bằng cách sử dụng @Spy
chú thích. Ngay cả khi tên thuộc tính là khác nhau.
@RunWith(MockitoJUnitRunner.class)
public class GameTest {
@Mock Player player;
@Spy List<String> enemies = new ArrayList<>();
@InjectMocks Game game;
@Test public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
enemies.add("Dragon");
enemies.add("Orc");
assertEquals(2, game.numberOfEnemies());
assertEquals("Player attack with: Sword", game.attack());
}
}
class Game {
private Player player;
private List<String> opponents;
public Game(Player player, List<String> opponents) {
this.player = player;
this.opponents = opponents;
}
public int numberOfEnemies() {
return opponents.size();
}
// ...
Đó là bởi vì Mockito sẽ kiểm tra Type Signature
lớp Game, Player
và List<String>
.
Trong lớp kiểm tra của bạn, lớp được kiểm tra nên được chú thích bằng @InjectMocks
. Điều này cho Mockito biết lớp nào sẽ đưa giả vào:
@InjectMocks
private SomeManager someManager;
Từ đó trở đi, chúng ta có thể chỉ định phương thức hoặc đối tượng cụ thể nào trong lớp, trong trường hợp này SomeManager
, sẽ được thay thế bằng giả:
@Mock
private SomeDependency someDependency;
Trong ví dụ này, SomeDependency
bên trong SomeManager
lớp sẽ bị chế giễu.
@Mock
chú thích chế giễu đối tượng liên quan.
@InjectMocks
chú thích cho phép tiêm vào đối tượng cơ bản các giả định khác nhau (và có liên quan) được tạo bởi @Mock
.
Cả hai đều bổ sung.
@InjectMocks
để xây dựng lớp này và cũng theo dõi nó.
Ví dụ
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Ở đây chúng ta cần lớp DAO cho lớp dịch vụ. Vì vậy, chúng tôi chế nhạo nó và đưa nó vào thể hiện của lớp dịch vụ. Tương tự như vậy, trong khuôn khổ mùa xuân tất cả các @Autowired đậu có thể được chế giễu bởi @Mock trong jUnits và tiêm vào đậu của bạn thông qua @InjectMocks.
MockitoAnnotations.initMocks(this)
phương thức khởi tạo các giả này và tiêm chúng cho mọi phương thức thử nghiệm vì vậy nó cần được gọi trong setUp()
phương thức.
Một "khung mô phỏng", mà Mockito dựa trên, là một khung cung cấp cho bạn khả năng tạo các đối tượng Mock (theo thuật ngữ cũ, các đối tượng này có thể được gọi là shunts, vì chúng hoạt động như các shunt cho chức năng phụ thuộc) Nói cách khác, một mock đối tượng được sử dụng để bắt chước đối tượng thực mà mã của bạn phụ thuộc vào, bạn tạo một đối tượng proxy với khung mô phỏng. Bằng cách sử dụng các đối tượng giả trong các thử nghiệm của bạn, về cơ bản bạn đang đi từ thử nghiệm đơn vị bình thường sang thử nghiệm tích hợp
Mockito là một khung kiểm tra mã nguồn mở cho Java được phát hành theo Giấy phép MIT, nó là một "khung mô phỏng", cho phép bạn viết các bài kiểm tra đẹp với API đơn giản và sạch sẽ. Có nhiều khung mô phỏng khác nhau trong không gian Java, tuy nhiên về cơ bản có hai loại khung đối tượng giả chính, các khung công tác giả được triển khai thông qua proxy và các khung được triển khai thông qua ánh xạ lại lớp.
Các khung tiêm phụ thuộc như Spring cho phép bạn tiêm các đối tượng proxy của mình mà không sửa đổi bất kỳ mã nào, đối tượng giả định mong muốn một phương thức nhất định được gọi và nó sẽ trả về kết quả mong đợi.
Các @InjectMocks
chú thích cố gắng để nhanh chóng các instance fields kiểm tra đối tượng và tiêm nhiễm chú thích với@Mock
hoặc @Spy
vào các lĩnh vực cá nhân của đối tượng thử nghiệm.
MockitoAnnotations.initMocks(this)
gọi, đặt lại đối tượng thử nghiệm và khởi tạo lại các giả, vì vậy hãy nhớ có cái này ở @Before
/ @BeforeMethod
chú thích của bạn .
Một lợi thế bạn có được với cách tiếp cận được đề cập bởi @Tom là bạn không phải tạo bất kỳ hàm tạo nào trong Trình quản lý, và do đó hạn chế các ứng dụng khách khởi tạo nó.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//You don't need to instantiate the SomeManager with default contructor at all
//SomeManager someManager = new SomeManager();
//Or SomeManager someManager = new SomeManager(someDependency);
//tests...
}
Cho dù đó là một thực hành tốt hay không phụ thuộc vào thiết kế ứng dụng của bạn.
Nhiều người đã đưa ra một lời giải thích tuyệt vời ở đây về @Mock
vs @InjectMocks
. Tôi thích nó, nhưng tôi nghĩ các bài kiểm tra và ứng dụng của chúng tôi nên được viết theo cách mà chúng ta không nên sử dụng @InjectMocks
.
Tham khảo để đọc thêm với các ví dụ: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock
được sử dụng để khai báo / giả định các tham chiếu của các bean phụ thuộc, trong khi @InjectMocks
được sử dụng để giả lập các bean mà thử nghiệm đang được tạo.
Ví dụ:
public class A{
public class B b;
public void doSomething(){
}
}
kiểm tra cho các lớp học A
:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
Chú thích @InjectMocks có thể được sử dụng để tự động đưa các trường giả vào một đối tượng thử nghiệm.
Trong ví dụ dưới đây @InjectMocks đã sử dụng để đưa bản đồ dữ liệu giả vào dữ liệu Thư viện.
@Mock
Map<String, String> dataMap ;
@InjectMocks
DataLibrary dataLibrary = new DataLibrary();
@Test
public void whenUseInjectMocksAnnotation_() {
Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");
assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
}
Lưu ý rằng @InjectMocks
sắp bị phản đối
deprecate @InjectMocks và lịch trình xóa trong Mockito 3/4
và bạn có thể theo dõi câu trả lời @avp và liên kết trên:
Tại sao bạn không nên sử dụng chú thích MethMocks cho Autowire Field
Mặc dù các câu trả lời ở trên đã được trình bày, tôi chỉ cố gắng thêm chi tiết phút mà tôi thấy thiếu. Lý do đằng sau họ (The Why).
Hình minh họa:
Sample.java
---------------
public class Sample{
DependencyOne dependencyOne;
DependencyTwo dependencyTwo;
public SampleResponse methodOfSample(){
dependencyOne.methodOne();
dependencyTwo.methodTwo();
...
return sampleResponse;
}
}
SampleTest.java
-----------------------
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class SampleTest{
@InjectMocks
Sample sample;
@Mock
DependencyOne dependencyOne;
@Mock
DependencyTwo dependencyTwo;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
public void sampleMethod1_Test(){
//Arrange the dependencies
DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();
DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();
//call the method to be tested
SampleResponse sampleResponse = sample.methodOfSample()
//Assert
<assert the SampleResponse here>
}
}