Różnica między @Mock i @InjectMocks


Odpowiedzi:


542

@Mocktworzy próbkę. @InjectMockstworzy instancję klasy i wstrzykuje symulacje utworzone za pomocą @Mock(lub@Spy adnotacji ).

Pamiętaj, że musisz użyć @RunWith(MockitoJUnitRunner.class)lub Mockito.initMocks(this)zainicjować te próby i wstrzyknąć je.

@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {

    @InjectMocks
    private SomeManager someManager;

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

     //tests...

}

2
Krótka i zwięzła odpowiedź. Również pomocne;)
Chaklader Asfak Arefe

Czy działa to w przypadku zależności przechodnich lub tylko bezpośrednich członków?
Pierre Thibault,

@PierreThibault Wstrzykiwanie próbne działa tylko dla bezpośrednich członków, ale można ustawić próbkę,
Tom Verelst

1
czuję, że jest to o wiele jasne niż większość artykułu online .... że małe komentarze zapisują mój tyłek ...
IHC_Applroid 10.09

Mam elementy, których adnotacja @Mock nie może dostarczyć, np. Kontekst. Jak mogę to zapewnić dla klasy głównej?
Mahdi

220

To jest przykładowy kod dotyczący tego, jak @Mocki@InjectMocks działa.

Powiedzmy, że mamy Gamei Playerklasę.

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

Jak widzisz, Gameklasa musi Playerwykonać 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 wyśmiewa klasę Gracza, a jej zachowanie za pomocą wheni thenReturnmetody. Wreszcie, przy użyciu @InjectMocksMockito że położy Playersię Game.

Zauważ, że nie musisz nawet tworzyć new Gameobiektu. Mockito wstrzyknie to dla ciebie.

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

Za pomocą @Spyadnotacji otrzymamy to samo zachowanie . Nawet jeśli nazwa atrybutu jest inna.

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

  // ...

To dlatego, że Mockito sprawdzi Type Signatureklasę gry, czyli Playeri List<String>.


16
W tym przykładzie powinna to być zaakceptowana odpowiedź.
AnnaKlein,

4
Myślę, że to też najlepsza odpowiedź
Evgeniy Dorofeev

4
Myślę, że to najlepsza odpowiedź potrójna.
Harvey Dent

1
Czasami testowanie z kpiną jest trudne do zrozumienia i zaprojektowania dla klasy. Jednak ten przykład bardzo pomaga w omówieniu.
Chaklader Asfak Arefe

1
Wielkie dzięki :) Do rzeczy z lepszym wyjaśnieniem.
Rishi,

80

W klasie testowej testowaną klasę należy opatrzyć adnotacjami @InjectMocks. Mówi to Mockito, do której klasy wstrzykiwać próbne:

@InjectMocks
private SomeManager someManager;

Od tego momentu możemy określić, które konkretne metody lub obiekty w klasie, w tym przypadku SomeManager, zostaną zastąpione próbnymi:

@Mock
private SomeDependency someDependency;

W tym przykładzie SomeDependencywewnątrz SomeManagerklasy będą wyśmiewani.


6
czy to zadziała, jeśli jakiś menedżer ma więcej niż jednego konstruktora? co jeśli jakiś menedżer miał 5 konstruktorów, skąd miałby wiedzieć, którego z nich chcesz użyć?
j2emanue

51

@Mock adnotacja wyśmiewa dany obiekt.

@InjectMocksadnotacja pozwala na wstrzyknięcie do obiektu bazowego różnych (i odpowiednich) próbnych stworzonych przez @Mock.

Oba są komplementarne.


1
Czy mogą być używane w tandemie na tym samym obiekcie?
IgorGanapolsky

1
Czy masz mini przykład swojego wymagania?
Mik378,

Mam klasę, w której trzeba szpiegować (za pośrednictwem Szpiega Mockito), a ta klasa ma konstruktora. Zastanawiałem się więc nad @InjectMocksstworzeniem tej klasy i szpiegowaniem jej.
IgorGanapolsky


23
  • @Mock tworzy próbną implementację dla potrzebnych klas.
  • @InjectMock tworzy instancję klasy i wstrzykuje do niej symulacje oznaczone adnotacjami @Mock .

Na przykład

@Mock
StudentDao studentDao;

@InjectMocks
StudentService service;

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

Tutaj potrzebujemy klasy DAO dla klasy usług. Wyśmiewamy go i wstrzykujemy do instancji klasy usług. Podobnie w systemie Spring wszystkie @Autowired bean mogą być wyśmiewane przez @Mock w jUnits i wstrzykiwane do twojej fasoli przez @InjectMocks.

MockitoAnnotations.initMocks(this)Metoda inicjuje te symulacje i wstrzykuje je dla każdej metody testowej, dlatego należy ją wywołać w setUp()metodzie.

Ten link zawiera dobry samouczek dotyczący frameworka Mockito


13

„Frameworka frameworka”, na której opiera się Mockito, jest frameworkiem, który daje możliwość tworzenia fałszywych obiektów (dawniej obiekty te można nazwać bocznikami, ponieważ działają one jako boczniki dla funkcji zależnej) Innymi słowy, próbka obiekt służy do naśladowania rzeczywistego obiektu, od którego zależy Twój kod, tworzysz obiekt proxy za pomocą frameworka. Używając próbnych obiektów w testach, zasadniczo przechodzisz od normalnego testowania jednostkowego do testowania integracyjnego

Mockito to otwarta platforma testowa dla Java wydana na licencji MIT, jest to „makieta frameworka”, która pozwala pisać piękne testy z czystym i prostym API. W przestrzeni Java jest wiele różnych fałszywych frameworków, jednak istnieją zasadniczo dwa główne typy fałszywych frameworków obiektowych, zaimplementowane za pośrednictwem proxy i zaimplementowane za pomocą mapowania klas.

Frameworki wstrzykiwania zależności, takie jak Spring, umożliwiają wstrzykiwanie obiektów proxy bez modyfikowania kodu, próbny obiekt oczekuje wywołania określonej metody i zwróci oczekiwany wynik.

@InjectMocksAdnotacja próbuje instancję testowania obiektów instancji i wstrzykuje pola oznaczone adnotacją z @Mocklub @Spydo prywatnych pól obiektu badań.

MockitoAnnotations.initMocks(this) zadzwoń, zresetuj obiekt testowy i ponownie zainicjuj próby, więc pamiętaj, aby mieć to przy sobie @Before / @BeforeMethodadnotacji.


2
Nie powiedziałbym, że „używając próbnych obiektów w swoich testach zasadniczo przechodzisz od normalnego testowania jednostkowego do testowania integracyjnego”. Dla mnie kpiną jest izolowanie testowanego urządzenia w celu przeprowadzenia testu jednostkowego. Testy integracyjne wykorzystają rzeczywiste, nie wyśmiewane zależności.
WesternGun

10

Jedną z zalet, które zyskujesz dzięki podejściu wspomnianemu przez @Tom, jest to, że nie musisz tworzyć żadnych konstruktorów w SomeManager, a zatem ograniczać klientów do tworzenia instancji.

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

}

To, czy jest to dobra praktyka, zależy od projektu aplikacji.


co gdyby jakiś menedżer miał 3 różne konstruktory, skąd wiedziałby, którego użyć?
j2emanue

W jaki sposób następnie weryfikujesz rzeczy w SomeManager, jeśli nie są one wyśmiewane?
IgorGanapolsky


4

@Mock służy do deklarowania / kpienia referencji zależnych ziaren, natomiast @InjectMocks służy do kpienia ziaren, dla których tworzony jest test.

Na przykład:

public class A{

   public class B b;

   public void doSomething(){

   }

}

test na klasę A:

public class TestClassA{

   @Mocks
   public class B b;

   @InjectMocks
   public class A a;

   @Test
   public testDoSomething(){

   }

}

4

Adnotacji @InjectMocks można używać do automatycznego wstrzykiwania próbnych pól do obiektu testowego.

W poniższym przykładzie @InjectMocks użył wstrzyknąć próbną dataMap do 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

Mimo że powyższe odpowiedzi obejmują, właśnie próbowałem dodać drobne szczegóły, których brakuje mi. Powód za nimi (Dlaczego).

wprowadź opis zdjęcia tutaj


Ilustracja:

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

Odniesienie

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.