Jaka jest różnica pomiędzy @Mock
i @InjectMocks
w ramach Mockito?
Jaka jest różnica pomiędzy @Mock
i @InjectMocks
w ramach Mockito?
Odpowiedzi:
@Mock
tworzy próbkę. @InjectMocks
tworzy 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...
}
To jest przykładowy kod dotyczący tego, jak @Mock
i@InjectMocks
działa.
Powiedzmy, że mamy Game
i Player
klasę.
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, Game
klasa musi Player
wykonać 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ą when
i thenReturn
metody. Wreszcie, przy użyciu @InjectMocks
Mockito że położy Player
się Game
.
Zauważ, że nie musisz nawet tworzyć new Game
obiektu. Mockito wstrzyknie to dla ciebie.
// you don't have to do this
Game game = new Game(player);
Za pomocą @Spy
adnotacji 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 Signature
klasę gry, czyli Player
i List<String>
.
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 SomeDependency
wewnątrz SomeManager
klasy będą wyśmiewani.
@Mock
adnotacja wyśmiewa dany obiekt.
@InjectMocks
adnotacja pozwala na wstrzyknięcie do obiektu bazowego różnych (i odpowiednich) próbnych stworzonych przez @Mock
.
Oba są komplementarne.
@InjectMocks
stworzeniem tej klasy i szpiegowaniem jej.
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
„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.
@InjectMocks
Adnotacja próbuje instancję testowania obiektów instancji i wstrzykuje pola oznaczone adnotacją z @Mock
lub @Spy
do 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
/ @BeforeMethod
adnotacji.
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.
Wielu ludzi dały wielką wyjaśnienie tutaj o @Mock
vs @InjectMocks
. Podoba mi się, ale uważam, że nasze testy i aplikacje powinny być napisane w taki sposób, abyśmy nie musieli ich używać@InjectMocks
.
Odniesienia do dalszego czytania z przykładami: https://tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@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(){
}
}
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"));
}
Zauważ, że @InjectMocks
wkrótce będą przestarzałe
przestarzałe @InjectMocks i harmonogram usuwania w Mockito 3/4
i możesz śledzić odpowiedź @avp i link na:
Dlaczego nie należy używać adnotacji InjectMocks do pól Autowire
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).
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>
}
}