Skip to content

Commit e832831

Browse files
committed
polymorphism published and mockito updated
1 parent 3c7befa commit e832831

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

_drafts/2019-01-21-mockito.md

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,184 @@ github: testing/tree/master/mockito
88
description: "Mockito"
99
keywords: "testowanie, testing, testy, jednostkowe, automatyczne, lokalne, instrumentalne, zaślepka, atrapa, unit test, mock, stub, junit, robolectric, mockito, android, programowanie, programming"
1010
---
11+
12+
## Dublerzy
13+
Testy jednostkowe rzadko kiedy dotyczą jednostki testowej wolnej od zależności innych obiektów co w konsekwencji prowadzi do definicji problemu tworzenia niezależnych testów na których rezultat nie ma wpływu żadna zależność testowanej jednostki. Wymaganą prawdziwą zależność można czasami dostarczyć ręcznie jednakże jest to kosztowne, obarczone marginesem błędu oraz często niewykonalne (szczególnie w przypadku testów instrumentalnych). Alternatywą dla tworzenia prawdziwych zależności jest dostarczanie `dublerów` (`double test`), które można sklasyfikować ze względu na przeznaczenie. Jedna z wielu definicji wyróżnia m.in. następujące obiekty: `dummy`, `fake`, `stub` i `mock`. `Dummy` jest przekazywany w celu spełnienia założeń sygnatury jednak jego metody nie są wywoływane. `Fake` posiada uproszczoną działającą implementacje spełniającą założenia interakcji. `Stub` dostarcza minimalną implementacją zależności, która pomija proces obliczeniowy i bezpośrednio zwraca zdefiniowany rezultat w taki sposób, aby test wykonał się pozytywnie. Natomiast `mock` to naiwna implementacja zależności, która bierze udział w procesie testowania i rejestruje nawiązane z nią interakcje, przeznaczona do badania zachowania. Mnogość definicji podziału dublerów oraz wynikające z tego różnice są powodem wielu niejasności, a sama terminologia jest mało istotna w procesie tworzenia zastępników. Pozwala on jednak zrozumieć co dokładnie i w jaki sposób ma zostać przetestowane. `Mockito` jest frameworkiem wspomagającym pisanie testów jednostkowych poprzez dostarczanie dublerów, które dzieli na zwyczajne `Mock` oraz częściowe `Spy`.
14+
15+
## Tworzenie
16+
Obiekt zastępnika oznaczony adnotacją `@Mock` lub `@Spy` może zostać inicjalizowany na kilka sposobów, m.in. poprzez uruchomienie klasy z użyciem `@RunWith(MockitoJUnitRunner.class)`, wykorzystanie zasady `MockitoJUnit.rule()` lub manualną inicjalizację za pomocą metody `MockitoAnnotations.initMocks()`. Instancję atrapy można uzyskać statyczną metodą `mock()` lub `spy()` co pozwala na pominięcie adnotacji.
17+
18+
{% highlight kotlin %}
19+
//use Mockito runner
20+
@RunWith(MockitoJUnitRunner::class)
21+
class InitMockTest {
22+
23+
@Mock
24+
var obj: Data? = null
25+
26+
@Test
27+
fun checkIsObjectInitialized() {
28+
assertNotNull(obj)
29+
}
30+
}
31+
32+
//or use Mockito rule
33+
class InitMockTest {
34+
35+
@Mock
36+
var obj: Data? = null
37+
38+
@get:Rule
39+
var mockitoRule = MockitoJUnit.rule()
40+
41+
@Test
42+
fun checkIsObjectInitialized() {
43+
assertNotNull(obj)
44+
}
45+
}
46+
47+
//or use manual init method
48+
class InitMockTest {
49+
50+
@Mock
51+
var obj: Data? = null
52+
53+
@Test
54+
fun checkIsObjectInitialized() {
55+
MockitoAnnotations.initMocks(this)
56+
assertNotNull(obj)
57+
}
58+
}
59+
60+
//or use static mock method
61+
class InitMockTest {
62+
63+
@Test
64+
fun checkIsObjectInitialized() {
65+
val obj = mock(Data::class.java)
66+
assertNotNull(obj)
67+
}
68+
}
69+
{% endhighlight %}
70+
71+
Obiekty dublerów w Mockito mogą zostać stworzone tylko dla typów własnych. Nie można zatem dostarczyć imitacji prymitywów. Co więcej należy przestrzegać zasady, że imitacji podlegają zależności testowanej jednostki, a nie sam testowany obiekt. Adnotacja `@InjectMocks` automatycznie wstrzykuje imitacje pól oznaczonych jako `@Mock` lub `@Spy` do obiektu co jest wykorzystywane jako alternatywa dla konstruktora w sytuacji tworzenia testowanej jednostki zależnej od innych atrap.
72+
73+
{% highlight kotlin %}
74+
@RunWith(MockitoJUnitRunner::class)
75+
class InjectMockTest {
76+
77+
@Mock
78+
var mock: Data? = null
79+
80+
//constructor requires Data arg
81+
@InjectMocks
82+
var manager1: Manager? = null
83+
84+
@Test
85+
fun checkIsManager1Initialized() {
86+
assertNotNull(mock)
87+
assertNotNull(manager1)
88+
}
89+
90+
//equivalent
91+
@Test
92+
fun checkIsManager2Initialized() {
93+
var manager2 = Manager(mock)
94+
assertNotNull(mock)
95+
assertNotNull(manager2)
96+
}
97+
}
98+
{% endhighlight %}
99+
100+
## Mock i Spy
101+
Mockito wprowadza podział zastępników na zwyczajne (`Mock`) oraz częściowe (`Spy`). `Mock` imituje wykonanie metod klasy bez wpływu na instancję atrapy. `Spy` wywołuje realną implementację instancji klasy dla wszystkich zachowań dla których nie została zdefiniowana naiwna implementacja zachowując przy tym właściwości atrapy (weryfikacja i imitacja).
102+
103+
{% highlight kotlin %}
104+
@RunWith(MockitoJUnitRunner::class)
105+
class MockSpyTest {
106+
107+
@Mock
108+
var mock: Data? = null
109+
110+
@Spy
111+
var spy: Data? = null
112+
113+
@Test
114+
fun checkIsObjectInitialized() {
115+
assertNotNull(mock)
116+
assertNotNull(spy)
117+
mock?.text = "mock"
118+
spy?.text = "spy"
119+
assertEquals("mock", mock?.text) //fails mock.text is null
120+
assertEquals("spy", spy?.text)
121+
}
122+
}
123+
{% endhighlight %}
124+
125+
## Konfiguracja
126+
Atrapy mogą być konfigurowane w taki sposób, aby zwracały zadaną wartość w zależności od metody i argumentów. W tym celu należy zdefiniować naiwną implementację m.in. za pomocą instrukcji `when.thenReturn` czy `doReturn.when`. Różnica między nimi polega na tym, że `when.thenReturn` sprawdza typy w trakcie kompilacji, pozwala na zwracanie wielu wartości i w gruncie rzeczy wykonuje wywołaną metodę co w konsekwencji może prowadzić do rzucenia wyjątku. Natomiast `doReturn.when` wspiera obiekty opakowań (`wrapper`) oznaczone jako `@Spy` i metody nie zwracające wartości oraz eliminuje problem wielu argumentów.
127+
128+
{% highlight kotlin %}
129+
@RunWith(MockitoJUnitRunner::class)
130+
class ConfigurationTest {
131+
132+
@Mock
133+
lateinit var mock: Data
134+
135+
@InjectMocks
136+
lateinit var manager: Manager
137+
138+
@Test
139+
fun checkFetchDataReturnsMockValue() {
140+
assertNull(manager.fetchData())
141+
`when`(mock.getInfo()).thenReturn("mock")
142+
assertNull(manager.fetchData()) //now it fails
143+
assertEquals("mock", manager.fetchData())
144+
}
145+
146+
@Test
147+
fun checkFetchDataReturnsMultipleMockValue() {
148+
assertNull(manager.fetchData())
149+
`when`(mock.getInfo()).thenReturn("mock1", "mock2")
150+
assertNull(manager.fetchData()) //now it fails
151+
assertEquals("mock1", manager.fetchData())
152+
assertEquals("mock2", manager.fetchData())
153+
assertEquals("mock2", manager.fetchData())
154+
}
155+
156+
@Test
157+
fun checkFetchDataTakesMultipleArgReturnsMockValue() {
158+
assertNull(manager.fetchData())
159+
doReturn("spy with arg").`when`(mock).getInfo("arg1")
160+
doReturn("spy with arg").`when`(mock).getInfo("arg2")
161+
assertEquals("spy with arg", manager.fetchDataWithMessage("arg1"))
162+
assertEquals("spy with arg", manager.fetchDataWithMessage("arg2"))
163+
//not possible without reset using when.thenReturn
164+
}
165+
166+
@Test
167+
fun checkGetInfoForWrappedReturnsWrapperValue() {
168+
var data = Data()
169+
var spyData = spy(data)
170+
doReturn("wrapper").`when`(spyData).getInfo()
171+
assertEquals("wrapper", manager.fetchData())
172+
}
173+
174+
@Test
175+
fun checkFetchDataWithArgsReturnsMockValue() {
176+
Mockito.`when`(mock.getInfo("arg")).thenReturn("mock with arg")
177+
assertEquals("mock with arg", manager.fetchDataWithMessage("arg"))
178+
assertEquals("mock with arg", manager.fetchDataWithMessage("whatever")) //fails
179+
`when`(mock.getInfo(anyString())).thenReturn("mock with arg")
180+
assertEquals("mock with arg", manager.fetchDataWithMessage("whatever")) //now it pass
181+
}
182+
183+
@Test(expected = IllegalArgumentException::class)
184+
fun checkFetchDataThrowsExceptionForSpecialCharactersArg() {
185+
`when`(mock.getInfo("@")).thenThrow(IllegalArgumentException())
186+
manager.fetchDataWithMessage("@")
187+
}
188+
}
189+
{% endhighlight %}
190+
191+
## Weryfikacja

0 commit comments

Comments
 (0)