Differenza tra @Mock e @InjectMocks


Risposte:


542

@Mockcrea un finto. @InjectMockscrea 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...

}

2
Risposta breve e concisa. Utile anche;)
Chaklader Asfak Arefe

Funziona per dipendenze transitive o solo membri diretti?
Pierre Thibault,

@PierreThibault Injecting mock funziona solo per i membri diretti, ma puoi impostare un mock per consentire stub profondi static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom Verelst

1
Sento che questo è molto chiaro rispetto alla maggior parte dell'articolo online .... che piccoli commenti mi salvano il culo ...
IHC_Applroid

Ho alcuni elementi che non possono essere forniti dall'annotazione @Mock come il contesto. Come posso fornirlo per la classe principale?
Mahdi,

220

Questo è un codice di esempio su come @Mocke@InjectMocks funziona.

Di 'che abbiamo Gamee Playerclasse.

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 Gameclasse deve Playereseguire 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 whene thenReturnmetodo. Infine, utilizzando @InjectMocksMockito metterà che Playerin Game.

Si noti che non è nemmeno necessario creare un new Gameoggetto. Mockito lo inietterà per te.

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

Otterremo lo stesso comportamento anche usando l' @Spyannotazione. 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 Signatureclasse Game, che è Playere List<String>.


16
Con questo esempio dovrebbe essere la risposta accettata.
AnnaKlein,

4
Penso che anche questo sia il miglior risponditore
Evgeniy Dorofeev il

4
Penso che questa sia la migliore risposta tripla.
Harvey Dent,

1
A volte trovo difficile capire e progettare i test con la beffa per una classe. Tuttavia, questo esempio aiuta molto a fornire una panoramica.
Chaklader Asfak Arefe,

1
Molte grazie :) Al punto con una spiegazione migliore.
Rishi,

80

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, SomeDependencyall'interno della SomeManagerclasse verrà deriso.


6
funzionerà se someManager ha più di un costruttore? cosa succederebbe se someManager avesse 5 costruttori come farebbe a sapere quale vuoi usare?
j2emanue,

51

@Mock l'annotazione prende in giro l'oggetto in questione.

@InjectMocksl'annotazione consente di iniettare nell'oggetto sottostante i diversi (e rilevanti) mock creati da @Mock.

Entrambi sono complementari.


1
Possono essere usati in tandem sullo stesso oggetto?
IgorGanapolsky,

1
Hai un mini esempio delle tue esigenze?
Mik378,

Ho una classe che deve essere spiata (tramite Mockito Spy) e questa classe ha un costruttore. Quindi stavo pensando di usare @InjectMocksper costruire questa classe e anche spiarla.
IgorGanapolsky,

1
È quello che stai cercando? stackoverflow.com/a/35969166/985949
Mik378

23
  • @Mock crea un'implementazione fittizia per le classi di cui hai bisogno.
  • @InjectMock crea un'istanza della classe e inietta le simulazioni contrassegnate con le annotazioni @Mock in essa.

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.

Questo link ha un buon tutorial per il framework Mockito


13

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' @InjectMocksannotazione tenta di creare un'istanza dell'istanza dell'oggetto test e inietta campi annotati con @Mocko @Spynei 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/ @BeforeMethodannotazione.


2
Non direi che "Usando oggetti simulati nei test si passa essenzialmente dai normali test unitari ai test integrativi". Per me, deridere è isolare l'apparecchiatura da testare per testare l'unità. Il test di integrazione utilizzerà dipendenze reali non derise.
WesternGun

10

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.


cosa succederebbe se someManager avesse 3 costruttori diversi, come farebbe a sapere quale usare?
j2emanue,

Come verifichi quindi cose su someManager se non sono derise?
IgorGanapolsky,


4

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

   }

}

4

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

3

Si noti che @InjectMocksstanno per essere deprecati

deprecate @InjectMocks e programmate la rimozione in Mockito 3/4

e puoi seguire la risposta @avp e link su:

Perché non si dovrebbe usare l'annotazione InjectMocks per i campi di Autowire


3

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

inserisci qui la descrizione dell'immagine


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

Riferimento

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.