Sự khác biệt giữa @Mock và @InjectMocks


Câu trả lời:


542

@Mocktạo ra một giả @InjectMockstạ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...

}

2
Câu trả lời ngắn gọn và súc tích. Hữu ích quá;)
Chaklader Asfak Arefe

Điều này làm việc cho phụ thuộc bắc cầu hoặc chỉ thành viên trực tiếp?
Pierre Thibault

@PierreThibault tiêm chích mocks chỉ hoạt động cho các thành viên trực tiếp, nhưng bạn có thể thiết lập một mô hình để cho phép khai sâu static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/...
Tom Verelst

1
tôi cảm thấy điều này rõ ràng hơn hầu hết các bài báo trực tuyến .... rằng những bình luận nhỏ đó đã cứu cái mông của tôi ...
IHC_Applroid

Tôi có một số mục không thể được cung cấp bởi chú thích @Mock như bối cảnh. Làm thế nào tôi có thể cung cấp điều đó cho lớp chính?
Mahdi

220

Đây là một mã mẫu về cách thức @Mock@InjectMockshoạt động.

Nói rằng chúng tôi có GamePlayerlớ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, Gamelớp cần Playerphả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 whenthenReturnphương thức. Cuối cùng, sử dụng @InjectMocksMockito sẽ đưa nó Playervà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 @Spychú 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 Signaturelớp Game, PlayerList<String>.


16
Với ví dụ này, nó sẽ là câu trả lời được chấp nhận.
AnnaKlein

4
Tôi nghĩ rằng đây cũng là một asnwer tốt nhất
Evgeniy Dorofeev

4
Tôi nghĩ rằng đây là câu trả lời tốt nhất gấp ba.
Harvey Dent

1
Đôi khi tôi thấy việc kiểm tra với sự chế giễu khó hiểu và thiết kế cho một lớp. Tuy nhiên, ví dụ này giúp rất nhiều để cung cấp tổng quan.
Chaklader Asfak Arefe

1
Rất cám ơn :) Đến điểm với lời giải thích tốt hơn.
Rishi

80

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, SomeDependencybên trong SomeManagerlớp sẽ bị chế giễu.


6
nó sẽ hoạt động nếu someManager có nhiều hơn một constructor? Điều gì xảy ra nếu someManager có 5 constructor làm sao nó biết cái nào bạn muốn sử dụng?
j2emanue

51

@Mock chú thích chế giễu đối tượng liên quan.

@InjectMockschú 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.


1
Chúng có thể được sử dụng song song trên cùng một đối tượng không?
IgorGanapolsky

1
Bạn có một ví dụ nhỏ về yêu cầu của bạn?
Mik378

Tôi có một lớp cần được theo dõi (thông qua Mockito Spy) và lớp này có một hàm tạo. Vì vậy, tôi đã nghĩ đến việc sử dụng @InjectMocksđể xây dựng lớp này và cũng theo dõi nó.
IgorGanapolsky

1
Có phải đó là những gì bạn đang tìm kiếm? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Mock tạo ra một triển khai giả cho các lớp bạn cần.
  • @InjectMock tạo một thể hiện của lớp và đưa các giả được đánh dấu bằng các chú thích @Mock vào 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.

Liên kết này có một hướng dẫn tốt cho khung Mockito


13

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 @InjectMockschú 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 @Spyvà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/ @BeforeMethodchú thích của bạn .


2
Tôi sẽ không nói rằ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". Đối với tôi, chế nhạo là cách ly vật cố định được thử nghiệm để kiểm tra đơn vị. Kiểm thử tích hợp sẽ sử dụng các phụ thuộc thực sự không bị chế giễu.
WesternGun

10

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.


Điều gì xảy ra nếu someManager có 3 constructor khác nhau, làm thế nào để biết cái nào sẽ sử dụng?
j2emanue

Làm thế nào để bạn xác minh công cụ trên someManager nếu nó không bị chế giễu?
IgorGanapolsky

5

Nhiều người đã đưa ra một lời giải thích tuyệt vời ở đây về @Mockvs @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/


1
Điều này dường như là một giải pháp lâu dài.
Vinayak Dornala

4

@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(){

   }

}

4

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"));
    }


3

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).

nhập mô tả hình ảnh ở đây


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>
    }
}

Tài liệu tham khảo

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.