Qual è la differenza tra @Mock
e @InjectMocks
nel framework Mockito?
Qual è la differenza tra @Mock
e @InjectMocks
nel framework Mockito?
Risposte:
@Mock
crea un finto. @InjectMocks
crea un'istanza della classe e inietta i mock creati con il @Mock
(o@Spy
) annotazioni in questa istanza.
Si noti che è necessario utilizzare @RunWith(MockitoJUnitRunner.class)
o Mockito.initMocks(this)
per inizializzare questi mock e iniettarli.
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
Questo è un codice di esempio su come @Mock
e@InjectMocks
funziona.
Di 'che abbiamo Game
e Player
classe.
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;
}
}
Come vedi, la Game
classe deve Player
eseguire un 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 deriderà una classe del Giocatore e il suo comportamento usando when
e thenReturn
metodo. Infine, utilizzando @InjectMocks
Mockito metterà che Player
in Game
.
Si noti che non è nemmeno necessario creare un new Game
oggetto. Mockito lo inietterà per te.
// you don't have to do this
Game game = new Game(player);
Otterremo lo stesso comportamento anche usando l' @Spy
annotazione. Anche se il nome dell'attributo è diverso.
@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();
}
// ...
Questo perché Mockito controllerà la Type Signature
classe Game, che è Player
e List<String>
.
Nella classe di test, la classe testata deve essere annotata con @InjectMocks
. Questo dice a Mockito in quale classe iniettare beffe:
@InjectMocks
private SomeManager someManager;
Da quel momento in poi, possiamo specificare quali metodi o oggetti specifici all'interno della classe, in questo caso SomeManager
, saranno sostituiti con mock:
@Mock
private SomeDependency someDependency;
In questo esempio, SomeDependency
all'interno della SomeManager
classe verrà deriso.
@Mock
l'annotazione prende in giro l'oggetto in questione.
@InjectMocks
l'annotazione consente di iniettare nell'oggetto sottostante i diversi (e rilevanti) mock creati da @Mock
.
Entrambi sono complementari.
@InjectMocks
per costruire questa classe e anche spiarla.
Per esempio
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
Qui abbiamo bisogno della classe DAO per la classe di servizio. Quindi, lo prendiamo in giro e lo iniettiamo nell'istanza della classe di servizio. Allo stesso modo, nel framework Spring tutti i bean @Autowired possono essere derisi da @Mock in jUnits e iniettati nel bean tramite @InjectMocks.
MockitoAnnotations.initMocks(this)
Il metodo inizializza questi mock e li inietta per ogni metodo di test, quindi deve essere chiamato nel setUp()
metodo.
Un "framework beffardo", su cui si basa Mockito, è un framework che ti dà la possibilità di creare oggetti Mock (in vecchi termini questi oggetti potrebbero essere chiamati shunt, poiché funzionano come shunt per funzionalità di dipendenza) In altre parole, un mock L'oggetto viene utilizzato per imitare l'oggetto reale da cui dipende il codice, si crea un oggetto proxy con il framework beffardo. Usando oggetti simulati nei test si passa essenzialmente dai normali test unitari ai test integrativi
Mockito è un framework di test open source per Java rilasciato sotto licenza MIT, è un "framework di simulazione", che consente di scrivere bellissimi test con API pulite e semplici. Esistono molti framework di derisione diversi nello spazio Java, tuttavia esistono essenzialmente due tipi principali di framework di oggetti simulati, quelli implementati tramite proxy e quelli implementati tramite il remapping di classe.
I framework di iniezione delle dipendenze come Spring consentono di iniettare gli oggetti proxy senza modificare alcun codice, l'oggetto simulato si aspetta che venga chiamato un determinato metodo e restituirà un risultato previsto.
L' @InjectMocks
annotazione tenta di creare un'istanza dell'istanza dell'oggetto test e inietta campi annotati con @Mock
o @Spy
nei campi privati dell'oggetto test.
MockitoAnnotations.initMocks(this)
chiama, reimposta l'oggetto di prova e reinizializza i mock, quindi ricorda di averlo nella tua @Before
/ @BeforeMethod
annotazione.
Un vantaggio che si ottiene con l'approccio citato da @Tom è che non è necessario creare alcun costruttore in SomeManager, e quindi limitare i client a crearne un'istanza.
@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...
}
Che si tratti di una buona pratica o meno dipende dalla progettazione dell'applicazione.
Molte persone hanno dato un grande spiegazione qui circa @Mock
vs @InjectMocks
. Mi piace, ma penso che i nostri test e le nostre applicazioni debbano essere scritti in modo tale da non dover essere utilizzati@InjectMocks
.
Riferimento per ulteriori letture con esempi: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@Mock
è usato per dichiarare / deridere i riferimenti dei bean dipendenti, mentre @InjectMocks
è usato per deridere il bean per il quale viene creato il test.
Per esempio:
public class A{
public class B b;
public void doSomething(){
}
}
test per classe A
:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
L'annotazione @InjectMocks può essere utilizzata per iniettare automaticamente campi simulati in un oggetto test.
Nell'esempio seguente @InjectMocks ha usato per iniettare la dataMap finta in dataLibrary.
@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"));
}
Sebbene le risposte di cui sopra siano state coperte, ho appena provato ad aggiungere dettagli minuti che vedo mancanti. Il motivo dietro di loro (Il perché).
Illustrazione:
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>
}
}