Was ist der Unterschied zwischen @Mock
und @InjectMocks
im Mockito-Framework?
@Mock
erstellt einen Schein. @InjectMocks
erstellt eine Instanz der Klasse und fügt die mit der Annotation @Mock
(oder @Spy
) erstellten Mocks in diese Instanz ein.
Beachten Sie, dass Sie @RunWith(MockitoJUnitRunner.class)
oder Mockito.initMocks(this)
verwenden müssen, um diese Mocks zu initialisieren und sie zu injizieren.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Dies ist ein Beispielcode für die Funktionsweise von @Mock
und @InjectMocks
.
Angenommen, wir haben die Klasse Game
und Player
.
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, benötigt Game
class Player
, um eine attack
auszuführen.
@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 verspottet eine Player-Klasse und ihr Verhalten mithilfe der when
- und thenReturn
-Methode. Mit @InjectMocks
setzt Mockito die Player
in Game
.
Beachten Sie, dass Sie nicht einmal ein new Game
-Objekt erstellen müssen. Mockito wird es für Sie spritzen.
// you don't have to do this
Game game = new Game(player);
Das gleiche Verhalten erhalten wir auch mit @Spy
Annotation. 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 den Type Signature
der Game-Klasse überprüft, nämlich Player
und List<String>
.
In Ihrer Testklasse sollte die getestete Klasse mit @InjectMocks kommentiert werden. 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 SomeManager , durch Mocks ersetzt werden:
@Mock
private SomeDependency someDependency;
In diesem Beispiel wird "SomeDependency" in der SomeManager-Klasse verspottet.
@Mock
-Annotation spottet das betroffene Objekt.
@InjectMocks
-Annotation ermöglicht das Einfügen der verschiedenen (und relevanten) Mocks, die von @Mock
erstellt wurden, in das zugrunde liegende Objekt.
Beide ergänzen sich.
Zum Beispiel
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Hier brauchen wir die Dao-Klasse für die Serviceklasse. Also spottet man es und injiziert es in die Service-Class-Instanz . Ebenso können alle @Autowired-Beans im Spring-Framework von @Mock in jUnits verspottet und über @InjectMocks in Ihre Bean injiziert werden.
Die MockitoAnnotations.initMocks (diese) Methode initialisiert diese Mocks und injiziert sie für jede Testmethode, sodass sie in der setUp-Methode aufgerufen werden muss.
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 arbeiten) 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 Mock-Objekten in Ihren Tests wechseln Sie im Wesentlichen vom normalen Unit-Test 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 mit sauberer und einfacher API schöne Tests schreiben können. Es gibt viele verschiedene Mocking-Frameworks im Java-Bereich, jedoch gibt es im Wesentlichen zwei Haupttypen von Mock-Objekt-Frameworks, solche, die über einen Proxy implementiert werden, und solche, die über die Klassenumbildung implementiert werden.
Abhängigkeitsinjektions-Frameworks wie Spring ermöglichen das Einfügen Ihrer Proxy-Objekte, ohne Code zu ändern. Das Mock-Objekt erwartet, dass eine bestimmte Methode aufgerufen wird, und liefert ein erwartetes Ergebnis.
Die Annotation @InjectMocks
versucht, die Instanz des Testobjekts zu instanziieren, und fügt mit @Mock
oder @Spy
kommentierte Felder in private Felder des Testobjekts ein.
MockitoAnnotations.initMocks(this)
call, setzt das Prüfobjekt zurück und initialisiert Mocks neu. Denken Sie also daran, dies an Ihrer Annotation @Before
/@BeforeMethod
zu haben.
Ein Vorteil, den Sie mit dem von @Tom genannten Ansatz erhalten, besteht darin, dass Sie im SomeManager keine Konstruktoren erstellen müssen und die Clients daher auf die Instanziierung beschränkt sind.
@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 es sich um eine bewährte Methode handelt, hängt von Ihrem Anwendungsdesign ab.
Viele Leute haben hier eine gute Erklärung über @Mock
vs @InjectMocks
gegeben. Ich mag es, aber ich denke, unsere Tests und Anwendungen sollten so geschrieben sein, dass wir @InjectMocks
nicht brauchen sollten.
Referenz zum weiteren Lesen mit Beispielen: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock wird verwendet, um die Referenzen der abhängigen Beans zu deklarieren/zu simulieren, während @InjectMocks verwendet wird, um die Bean zu simulieren, für die der Test erstellt wird.
Zum Beispiel:
public class A{
public class B b;
public void doSomething(){
}
}
test für Klasse A:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
Mit der @InjectMocks-Annotation können Scheinfelder automatisch in ein Testobjekt eingefügt werden.
Im folgenden Beispiel hat @InjectMocks verwendet, um die nachgebildete dataMap in die dataLibrary zu injizieren.
@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"));
}
Beachten Sie, dass @InjectMocks
gerade dabei ist, nicht mehr empfohlen zu werden
deprecate @InjectMocks und Zeitplan für die Entfernung in Mockito 3/4
und du kannst @avp antworten und verlinken weiter folgen:
Warum sollten Sie InjectMocks-Annotation nicht für automatische Felder verwenden?