Unterschied zwischen @Mock und @InjectMocks


Antworten:


542

@Mockschafft einen Schein. @InjectMocksErstellt eine Instanz der Klasse und fügt die mit den @Mock(oder @Spy) Annotationen erstellten Mocks in diese Instanz ein.

Beachten Sie, dass Sie diese Mocks verwenden @RunWith(MockitoJUnitRunner.class)oder Mockito.initMocks(this)initialisieren und injizieren müssen.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

    @Mock
    private SomeDependency someDependency; // this will be injected into someManager

     //tests...

}

2
Kurze und prägnante Antwort. Hilfreich auch;)
Chaklader Asfak Arefe

Funktioniert dies für transitive Abhängigkeiten oder nur für direkte Mitglieder?
Pierre Thibault

@PierreThibault Injecting Mocks funktioniert nur für direkte Mitglieder, aber Sie können ein Mock festlegen, um tiefe Stubs zuzulassen static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom Verelst

1
Ich denke, das ist viel klarer als die meisten Artikel online ... dass kleine Kommentare meinen Arsch retten ...
IHC_Applroid

Ich habe einige Elemente, die nicht durch @ Mock-Annotation bereitgestellt werden können, wie z. B. den Kontext. Wie kann ich das für die Hauptklasse bereitstellen?
Mahdi

220

Dies ist ein Beispielcode, wie @Mockund @InjectMocksfunktioniert.

Sagen wir, wir haben Gameund PlayerKlasse.

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

Wie Sie sehen, muss die GameKlasse eine Playerausführen 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 wird eine Spielerklasse verspotten und ihr Verhalten mit whenund thenReturnMethode. Schließlich verwendet @InjectMockswird Mockito setzen , dass Playerin Game.

Beachten Sie, dass Sie nicht einmal ein new GameObjekt erstellen müssen . Mockito wird es für Sie injizieren.

// you don't have to do this
Game game = new Game(player);

Wir werden das gleiche Verhalten auch mit @SpyAnmerkungen erhalten. Auch wenn der Attributname unterschiedlich ist.

@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();
  }

  // ...

Das liegt daran, dass Mockito die Type Signatureof Game-Klasse überprüft , die Playerund ist List<String>.


16
In diesem Beispiel sollte es die akzeptierte Antwort sein.
AnnaKlein

4
Ich denke, dies ist auch die beste Antwort
Evgeniy Dorofeev

4
Ich denke, das ist die beste dreifache Antwort.
Harvey Dent

1
Manchmal finde ich das Testen mit dem Spott schwer zu verstehen und für eine Klasse zu entwerfen. Dieses Beispiel hilft jedoch sehr, den Überblick zu geben.
Chaklader Asfak Arefe

1
Vielen Dank :) Auf den Punkt mit besserer Erklärung.
Rishi

80

In Ihrer Testklasse sollte die getestete Klasse mit Anmerkungen versehen sein @InjectMocks. Dies teilt Mockito mit, in welche Klasse Mocks injiziert werden sollen:

@InjectMocks
private SomeManager someManager;

Von da an können wir angeben, welche spezifischen Methoden oder Objekte innerhalb der Klasse in diesem Fall SomeManagerdurch Mocks ersetzt werden sollen:

@Mock
private SomeDependency someDependency;

In diesem Beispiel wird SomeDependencyinnerhalb der SomeManagerKlasse verspottet.


6
Funktioniert dies, wenn someManager mehr als einen Konstruktor hat? Was wäre, wenn someManager 5 Konstruktoren hätte? Wie würde es wissen, welchen Sie verwenden möchten?
j2emanue

51

@Mock Annotation verspottet das betroffene Objekt.

@InjectMocksMit Annotation können die verschiedenen (und relevanten) Mocks, die von erstellt wurden, in das zugrunde liegende Objekt eingefügt werden @Mock.

Beide ergänzen sich.


1
Können sie zusammen für dasselbe Objekt verwendet werden?
IgorGanapolsky

1
Haben Sie ein kleines Beispiel für Ihre Anforderung?
Mik378

Ich habe eine Klasse, die ausspioniert werden muss (über Mockito Spy), und diese Klasse hat einen Konstruktor. Also dachte ich darüber nach @InjectMocks, diese Klasse zu konstruieren und sie auch auszuspionieren.
IgorGanapolsky

1
Ist es das, wonach du suchst? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Spotten erstellt eine für die benötigten Klassen.
  • @InjectMock erstellt eine Instanz der Klasse und fügt die mit den Anmerkungen @Mock gekennzeichneten Mocks ein .

Zum Beispiel

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

Hier benötigen wir die DAO-Klasse für die Serviceklasse. Also verspotten wir es und fügen es in die Serviceklasseninstanz ein. In ähnlicher Weise können im Spring-Framework alle @ Autowired- Beans verspottet werden @Mock in jUnits @InjectMocks in Ihre Bean injiziert werden.

MockitoAnnotations.initMocks(this) Die Methode initialisiert diese Mocks und injiziert sie für jede Testmethode, sodass sie in der aufgerufen werden muss setUp() Methode werden muss.

Dieser Link enthält ein gutes Tutorial für das Mockito-Framework


13

Ein "Mocking Framework", auf dem Mockito basiert, ist ein Framework, mit dem Sie Mock-Objekte erstellen können (in alten Begriffen könnten diese Objekte Shunts genannt werden, da sie als Shunts für abhängige Funktionen fungieren). Mit anderen Worten, ein Mock Objekt wird verwendet, um das reale Objekt zu imitieren, von dem Ihr Code abhängig ist. Sie erstellen ein Proxy-Objekt mit dem Mocking-Framework. Durch die Verwendung von Scheinobjekten in Ihren Tests wechseln Sie im Wesentlichen vom normalen Komponententest zum Integrationstest

Mockito ist ein Open-Source-Testframework für Java, das unter der MIT-Lizenz veröffentlicht wurde. Es ist ein "Mocking-Framework", mit dem Sie schöne Tests mit einer sauberen und einfachen API schreiben können. Es gibt viele verschiedene Mocking-Frameworks im Java-Bereich, es gibt jedoch im Wesentlichen zwei Haupttypen von Mock-Objekt-Frameworks, solche, die über Proxy implementiert werden, und solche, die über Klassen-Remapping implementiert werden.

Abhängigkeitsinjektions-Frameworks wie Spring ermöglichen es Ihnen, Ihre Proxy-Objekte zu injizieren, ohne Code zu ändern. Das Mock-Objekt erwartet, dass eine bestimmte Methode aufgerufen wird, und gibt ein erwartetes Ergebnis zurück.

Die @InjectMocksAnnotation versucht, die Testobjektinstanz zu instanziieren und fügt mit @Mockoder @Spyin private Felder des Testobjekts annotierte Felder ein.

MockitoAnnotations.initMocks(this)Rufen Sie auf, setzen Sie das Testobjekt zurück und initialisieren Sie die Mocks neu. Denken Sie also daran, dies bei Ihrer @Before/ @BeforeMethodAnnotation zu haben.


2
Ich würde nicht sagen, dass "Durch die Verwendung von Scheinobjekten in Ihren Tests Sie im Wesentlichen vom normalen Komponententest zum Integrationstest übergehen". Für mich bedeutet Verspotten, die zu testende Vorrichtung zu isolieren, um einen Unit-Test durchzuführen. Beim Integrationstest werden echte, nicht verspottete Abhängigkeiten verwendet.
WesternGun

10

Ein Vorteil des von @Tom erwähnten Ansatzes besteht darin, dass Sie im SomeManager keine Konstruktoren erstellen müssen und somit die Clients einschränken müssen, um ihn zu instanziieren.

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

}

Ob dies eine gute Vorgehensweise ist oder nicht, hängt von Ihrem Anwendungsdesign ab.


Was wäre, wenn someManager 3 verschiedene Konstruktoren hätte? Wie würde es wissen, welchen es verwenden soll?
j2emanue

Wie überprüft man dann etwas auf someManager, wenn es nicht verspottet wird?
IgorGanapolsky


4

@Mockwird verwendet, um die Referenzen der abhängigen Beans zu deklarieren / zu verspotten, während @InjectMocksdie Bean verspottet wird, für die ein Test erstellt wird.

Zum Beispiel:

public class A{

   public class B b;

   public void doSomething(){

   }

}

Test für die Klasse A:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

Die Annotation @InjectMocks kann verwendet werden, um Scheinfelder automatisch in ein Testobjekt einzufügen.

Im folgenden Beispiel hat @InjectMocks verwendet, um die nachgebildete dataMap in die dataLibrary einzufügen.

@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

Obwohl die obigen Antworten behandelt wurden, habe ich nur versucht, kleinste Details hinzuzufügen, die mir fehlen. Der Grund dahinter (The Why).

Geben Sie hier die Bildbeschreibung ein


Illustration:

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

Referenz

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.