Diferença entre @Mock e @InjectMocks


Respostas:


542

@Mockcria uma farsa. @InjectMockscria uma instância da classe e injeta as simulações criadas com as anotações @Mock(ou @Spy) nessa instância.

Observe que você deve usar @RunWith(MockitoJUnitRunner.class)ou Mockito.initMocks(this)para inicializar essas simulações e injetá-las.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

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

     //tests...

}

2
Resposta curta e concisa. Também útil;)
Chaklader Asfak Arefe

Isso funciona para dependências transitivas ou apenas para membros diretos?
Pierre Thibault

@PierreThibault A injeção de zombarias funciona apenas para membros diretos, mas você pode definir um mock para permitir stubs profundos static.javadoc.io/org.mockito/mockito-core/3.0.0/org/mockito/…
Tom Verelst

1
Eu sinto que este é muito mais clara do que a maioria do artigo on-line .... que pequenos comentários salvar a minha bunda ...
IHC_Applroid

Eu tenho alguns itens que não podem ser fornecidos pela anotação @Mock como contexto. Como posso fornecer isso para a classe principal?
Mahdi

220

Este é um código de exemplo de como @Mocke @InjectMocksfunciona.

Digamos que temos 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;
    }
}

Como você vê, a Gameclasse precisa Playerexecutar um 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 irá zombar de uma classe Player e seu comportamento usando whene thenReturnmétodo. Por fim, usar o @InjectMocksMockito colocará isso Playerem Game.

Observe que você nem precisa criar um new Gameobjeto. Mockito irá injetá-lo para você.

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

Também obteremos o mesmo comportamento usando a @Spyanotação. Mesmo se o nome do atributo for diferente.

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

  // ...

Isso porque Mockito irá verificar a Type Signatureclasse Game, que é Playere List<String>.


16
Neste exemplo, deve ser a resposta aceita.
AnnaKlein

4
Eu acho que este é o melhor asnwer também #
Evgeniy Dorofeev

4
Eu acho que esta é a melhor resposta tripla.
Harvey Dent

1
Às vezes, acho que é difícil entender e projetar os testes com a zombaria para uma classe. No entanto, este exemplo ajuda muito a fornecer a visão geral.
Chaklader Asfak Arefe

1
Muito obrigado :) Ao ponto com uma melhor explicação.
Rishi

80

Na sua classe de teste, a classe testada deve ser anotada @InjectMocks. Isso informa ao Mockito em qual classe injetar zombarias:

@InjectMocks
private SomeManager someManager;

A partir de então, podemos especificar quais métodos ou objetos específicos dentro da classe, neste caso SomeManager, serão substituídos por zombarias:

@Mock
private SomeDependency someDependency;

Neste exemplo, SomeDependencydentro da SomeManagerclasse será ridicularizado.


6
isso funcionará se algumManager tiver mais de um construtor? e se algumManager tivesse 5 construtores, como saberia qual deles você deseja usar?
J2emanue

51

@Mock anotação zomba do objeto em questão.

@InjectMocksA anotação permite injetar no objeto subjacente as diferentes (e relevantes) simulações criadas por @Mock.

Ambos são complementares.


1
Eles podem ser usados ​​em conjunto no mesmo objeto?
IgorGanapolsky

1
Você tem um mini-exemplo de sua exigência?
Mik378

Eu tenho uma classe que precisa ser espionada (via Mockito Spy), e essa classe tem um construtor. Então, eu estava pensando em usar @InjectMockspara construir essa classe e espioná-la também.
IgorGanapolsky

1
É isso que você está procurando? stackoverflow.com/a/35969166/985949
Mik378

23
  • O @Mock cria uma implementação simulada para as classes que você precisa.
  • O @InjectMock cria uma instância da classe e injeta as simulações marcadas com as anotações @Mock .

Por exemplo

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

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

Aqui precisamos da classe DAO para a classe de serviço. Portanto, zombamos e injetamos na instância da classe de serviço. Da mesma forma, no framework Spring, todos os beans @Autowired podem ser zombados pelo @Mock nas jUnits e injetados no seu bean através do @InjectMocks.

MockitoAnnotations.initMocks(this)O método inicializa essas simulações e as injeta em todos os métodos de teste, portanto, ele precisa ser chamado no setUp()método.

Este link tem um bom tutorial para o framework Mockito


13

Uma "estrutura de zombaria", na qual o Mockito se baseia, é uma estrutura que permite criar objetos Mock (em termos antigos, esses objetos podem ser chamados de desvios, pois funcionam como desvios para a funcionalidade dependente) Em outras palavras, um mock objeto é usado para imitar o objeto real do qual seu código depende, você cria um objeto proxy com a estrutura de simulação. Ao usar objetos simulados em seus testes, você basicamente passa do teste de unidade normal para o teste de integração

O Mockito é uma estrutura de teste de código aberto para Java lançada sob a licença MIT, é uma "estrutura de zombaria", que permite escrever belos testes com API limpa e simples. Existem muitas estruturas de simulação diferentes no espaço Java, no entanto, existem essencialmente dois tipos principais de estruturas de objetos de simulação, aquelas implementadas via proxy e aquelas implementadas via remapeamento de classe.

Estruturas de injeção de dependência como o Spring permitem que você injete seus objetos proxy sem modificar nenhum código, o objeto simulado espera que um determinado método seja chamado e retornará um resultado esperado.

A @InjectMocksanotação tenta instanciar a instância do objeto de teste e injeta campos anotados com @Mockou @Spyem campos particulares do objeto de teste.

MockitoAnnotations.initMocks(this)chame, redefina o objeto de teste e reinicialize as simulações; lembre-se de tê-lo em sua @Before/ @BeforeMethodanotação.


2
Eu não diria que "Ao usar objetos simulados em seus testes, você está basicamente passando do teste de unidade normal para o teste de integração". Para mim, zombar é isolar o equipamento a ser testado para fazer o teste unitário. O teste de integração usará dependências reais e não simuladas.
WesternGun 13/07/2018

10

Uma vantagem que você obtém com a abordagem mencionada pelo @Tom é que você não precisa criar construtores no SomeManager e, portanto, limitar os clientes a instanciar.

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

}

Se é uma boa prática ou não, depende do design do seu aplicativo.


e se algumManager tivesse três construtores diferentes, como saberia qual deles usar?
J2emanue

Como você verifica as coisas no someManager se elas não são zombadas ?
IgorGanapolsky


4

@Mocké usado para declarar / zombar das referências dos beans dependentes, enquanto @InjectMocksé usado para zombar do bean para o qual o teste está sendo criado.

Por exemplo:

public class A{

   public class B b;

   public void doSomething(){

   }

}

teste para classe A:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

A anotação @InjectMocks pode ser usada para injetar campos simulados em um objeto de teste automaticamente.

No exemplo abaixo, @InjectMocks usou para injetar o dataMap simulado na 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

Observe que @InjectMocksestá prestes a ser preterido

descontinuar o @InjectMocks e agendar a remoção no Mockito 3/4

e você pode seguir a resposta @avp e o link :

Por que você não deve usar a anotação InjectMocks para campos de ligação automática


3

Embora as respostas acima tenham sido abordadas, apenas tentei adicionar detalhes minuciosos que vejo faltando. A razão por trás deles (The Why).

insira a descrição da imagem aqui


Ilustração:

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

Referência

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.