Was ist der Unterschied zwischen @Mock
und @InjectMocks
im Mockito-Framework?
Was ist der Unterschied zwischen @Mock
und @InjectMocks
im Mockito-Framework?
Antworten:
@Mock
schafft einen Schein. @InjectMocks
Erstellt 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...
}
Dies ist ein Beispielcode, wie @Mock
und @InjectMocks
funktioniert.
Sagen wir, wir haben Game
und Player
Klasse.
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 Game
Klasse eine Player
ausfü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 when
und thenReturn
Methode. Schließlich verwendet @InjectMocks
wird Mockito setzen , dass Player
in Game
.
Beachten Sie, dass Sie nicht einmal ein new Game
Objekt 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 @Spy
Anmerkungen 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 Signature
of Game-Klasse überprüft , die Player
und ist List<String>
.
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 SomeManager
durch Mocks ersetzt werden sollen:
@Mock
private SomeDependency someDependency;
In diesem Beispiel wird SomeDependency
innerhalb der SomeManager
Klasse verspottet.
@Mock
Annotation verspottet das betroffene Objekt.
@InjectMocks
Mit 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.
@InjectMocks
, diese Klasse zu konstruieren und sie auch auszuspionieren.
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
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 @InjectMocks
Annotation versucht, die Testobjektinstanz zu instanziieren und fügt mit @Mock
oder @Spy
in 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
/ @BeforeMethod
Annotation zu haben.
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.
Viele Leute haben hier eine großartige Erklärung über @Mock
vs gegeben @InjectMocks
. Ich mag es, aber ich denke, unsere Tests und Anwendungen sollten so geschrieben sein, dass wir sie nicht brauchen sollten@InjectMocks
.
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 verspotten, während @InjectMocks
die 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(){
}
}
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"));
}
Beachten Sie, dass @InjectMocks
dies bald veraltet sein wird
Verwerfen Sie @InjectMocks und planen Sie das Entfernen in Mockito 3/4
und Sie können @avp Antwort folgen und auf verlinken :
Warum Sie InjectMocks Annotation nicht für Autowire-Felder verwenden sollten
Obwohl die obigen Antworten behandelt wurden, habe ich nur versucht, kleinste Details hinzuzufügen, die mir fehlen. Der Grund dahinter (The Why).
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>
}
}