Skip to content

Commit 975b1a3

Browse files
committed
delegation post published and robolectric updated
1 parent 88e4ddf commit 975b1a3

File tree

2 files changed

+87
-9
lines changed

2 files changed

+87
-9
lines changed

_drafts/2019-01-28-robolectric.md

Lines changed: 87 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,43 @@ categories: ["Testowanie"]
66
image: testing/robolectric
77
github: testing/tree/master/robolectric
88
description: "Robolectric"
9-
keywords: "testowanie, testing, testy, jednostkowe, automatyczne, instrumentalne, zaślepka, atrapa, unit test, mock, stub, robolectric, android, programowanie, programming"
9+
keywords: "testowanie, testing, testy, jednostkowe, automatyczne, lokalne, instrumentalne, zaślepka, atrapa, unit test, mock, stub, robolectric, robolectrictestrunner, config, shadow, build, setup, controller, activitycontroller, activity, fragment, service, intent, contenrprovider, android, programowanie, programming"
1010
---
1111

1212
## Przeznaczenie
13-
Uruchamianie jednostkowych testów instrumentalnych na urządzeniu lub emulatorze jest kosztowne i powolne. Jednakże w wielu sytuacjach nie sposób uciec od testowania logiki kodu powiązanego z komponentami Androida co sprawia, że testy instrumentalne są stosowane pomimo wynikających kosztów. Alternatywnym sposobem testowania kodu zależnego od `Android SDK` może być użycie framework `Robolectric`, który umożliwia przeprowadzanie `lokalnych testów` jednostkowych w środowisku wykonawyczm `JVM` na komponentach Android takich jak m.in. `Activity`, `Fragment`, `Intent`, `Service`, `ContentProvider`. W przeciwieństwie do testów instrumentalnych Robolectric wykonuje się piaskownicy systemowej (`sandbox`) co pozwala na konfiguracje środowiska Android dla wszystkich testów. Robolectric dostarcza również dublerów komponentów Android dla których nie potrafi dokonać tłumaczenia do testów jednostkowych (np. sensory hardware, usługi systemowe). Obsługuje inflację widoków, ładowanie zasobów i wiele innych aspektów zaimplementowanych w natywnym kodzie na urządzeniach co pozwala na wykonanie większości czynności jak na prawidzwym urządzeniu. Robolectric w łatwy sposób umożliwia zapewnienie własnej implementacji wybranych metod Android SDK dzięki czemu możliwa jest np. symulacja warunków czy zachowań sensorów. Wykorzystanie Robolectric jest bliższe podejściu testowania czarnoskrzynkowego (skupionego na zachowaniu) i nie wyklucza jednoczesnego stosowania innych bibliotek naiwnych implementacji takich jak np. `Mockito`.
13+
Uruchamianie jednostkowych testów instrumentalnych na urządzeniu lub emulatorze jest kosztowne i powolne. Jednakże w wielu sytuacjach nie sposób uciec od testowania logiki kodu powiązanego z komponentami Androida co sprawia, że testy instrumentalne są stosowane pomimo wynikających kosztów. Alternatywnym sposobem testowania kodu zależnego od `Android SDK` może być użycie framework `Robolectric`, który umożliwia przeprowadzanie `lokalnych testów` jednostkowych w środowisku wykonawyczm `JVM` na komponentach Android takich jak m.in. `Activity`, `Fragment`, `Intent`, `Service` czy `ContentProvider`. W przeciwieństwie do testów instrumentalnych Robolectric wykonuje się piaskownicy systemowej (`sandbox`) co pozwala na konfiguracje środowiska Android dla wszystkich testów. Robolectric dostarcza również `dublerów` komponentów Android dla których nie potrafi dokonać tłumaczenia do testów jednostkowych (np. sensory hardware, usługi systemowe). Obsługuje inflację widoków, ładowanie zasobów i wiele innych aspektów zaimplementowanych w natywnym kodzie na urządzeniach co pozwala na wykonanie większości czynności tak jak na prawidzwym urządzeniu. Robolectric w łatwy sposób pozwala na zapewnienie własnej implementacji wybranych metod Android SDK dzięki czemu możliwa jest np. symulacja warunków czy zachowań sensorów. Wykorzystanie Robolectric jest bliższe podejściu testowania czarnoskrzynkowego (skupionego na zachowaniu) i nie wyklucza jednoczesnego stosowania innych bibliotek naiwnych implementacji takich jak np. `Mockito`.
14+
15+
>**Przykład**
16+
>Na podstawie Aktywności `MainActivity` zostaną przedstawione możliwości implementacji testów jednostkowych w Robolectric.
17+
18+
{% highlight kotlin %}
19+
class MainActivity : AppCompatActivity() {
20+
21+
companion object {
22+
const val TITLE_KEY = "TITLE"
23+
}
24+
25+
override fun onCreate(savedInstanceState: Bundle?) {
26+
super.onCreate(savedInstanceState)
27+
setContentView(R.layout.activity_main)
28+
29+
buttonAction.setOnClickListener {
30+
val intent = Intent(this, SecondActivity::class.java)
31+
startActivity(intent)
32+
}
33+
34+
textViewTitle.setOnClickListener { textViewTitle.setText(R.string.clicked) }
35+
}
36+
37+
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
38+
super.onRestoreInstanceState(savedInstanceState)
39+
savedInstanceState?.let {
40+
if(it.containsKey(TITLE_KEY))
41+
textViewTitle.text = it.getString(TITLE_KEY)
42+
}
43+
}
44+
}
45+
{% endhighlight %}
1446

1547
## Uruchomienie
1648
Aby uruchomić test w Robolectric należy opatrzeć klasę testową adnotacją `@RunWith(RobolectricTestRunner.class)` oraz uzyskać instancję żądanej klasy komponentu.
@@ -50,12 +82,8 @@ class SimpleTest {
5082
}
5183
{% endhighlight %}
5284

53-
54-
## Shadow
55-
56-
5785
## Konfiguracja
58-
Aby zmienić domyślną konfiguracje testów należy oznaczyć klasy lub metody adnotacją `@Config` wraz z deklaracją konfiguracji. Metody z adnotacją `@Config` nadpisują zachowanie poziomu klasy. Konfiguracji mogą podlegać m.in. poziom `sdk`, plik `manifest`, klasy `shadows`, ścieżki do zasobów czy `kwalifikatory` (np. języka, regionu itp).
86+
Aby zmienić domyślną konfiguracje testów należy oznaczyć klasy lub metody adnotacją `@Config` wraz z deklaracją konfiguracji. Metody z adnotacją `@Config` nadpisują zachowanie z poziomu klasy. Konfiguracji mogą podlegać m.in. poziom `sdk`, plik `manifest`, klasy `shadows`, ścieżki do zasobów czy `kwalifikatory` (np. języka, regionu itp).
5987

6088
{% highlight kotlin %}
6189
@RunWith(RobolectricTestRunner::class)
@@ -106,7 +134,7 @@ class ConfigurationTest {
106134
{% endhighlight %}
107135

108136
## Cykl życia
109-
`ActivityController` odpowiada za tworzenie oraz zarządzanie cyklem życia tworzonej Aktywności. Metoda `buildActivity` tworzy instancję `ActivityController`, która umożliwia wywołanie wybranych metod cyklu życia oraz pobranie instancji Aktywności. Aby bezpośrednio uzyskać obiekt Aktywności z przebytym pełnym cyklem tworzenia należy wywołać metodę `setupActivity`.
137+
`ActivityController` odpowiada za tworzenie oraz zarządzanie cyklem życia tworzonej Aktywności. Metoda `buildActivity` tworzy instancję `ActivityController`, która umożliwia wywołanie wybranych metod cyklu życia (`create`, `start`, `resume`, `pause`, `stop`, `destroy`) oraz pobranie instancji Aktywności dla bieżącego stanu. Aby bezpośrednio uzyskać obiekt Aktywności z przebytym pełnym cyklem tworzenia należy wywołać metodę `setupActivity`.
110138

111139
{% highlight kotlin %}
112140
@RunWith(RobolectricTestRunner::class)
@@ -132,14 +160,64 @@ class LifecycleTest {
132160

133161
assertEquals(Lifecycle.State.CREATED, activity.lifecycle.currentState)
134162
//do more assertion for this state
163+
135164
controller.start()
136165
assertEquals(Lifecycle.State.STARTED, activity.lifecycle.currentState)
137166
//do more assertion for this state
167+
138168
controller.resume()
139169
assertEquals(Lifecycle.State.RESUMED, activity.lifecycle.currentState)
140170
//do more assertion for this state
171+
141172
controller.pause().stop().destroy()
142173
assertEquals(Lifecycle.State.DESTROYED, activity.lifecycle.currentState)
143174
}
175+
176+
@Test
177+
fun checkMainActivityHasWorkingRestoringState() {
178+
val savedInstanceState = Bundle()
179+
savedInstanceState.putString(TITLE_KEY, "value restored")
180+
val activity = Robolectric.buildActivity(MainActivity::class.java)
181+
.create().restoreInstanceState(savedInstanceState).get()
182+
183+
assertEquals("value restored", activity.textViewTitle.text)
184+
}
144185
}
145-
{% endhighlight %}
186+
{% endhighlight %}
187+
188+
W analogiczny sposób można zarządzać innymi kompontentami Androida i ich cyklem życia poprzez operowanie na dedykowanym kontrolerze (`ServiceController`, `FragmentController`, `ContentProviderController` itp.) oraz wykonanie metod odpowiadających ich cyklowi życia.
189+
190+
{% highlight kotlin %}
191+
@RunWith(RobolectricTestRunner::class)
192+
class ServiceTest {
193+
194+
@Test
195+
fun checkSomeServiceProperlyBindAndUnbind() {
196+
val service = Robolectric.buildService(SomeService::class.java).bind().get()
197+
assertTrue(service.bound)
198+
service.onUnbind(null)
199+
assertFalse(service.bound)
200+
}
201+
}
202+
203+
//this is only dummy implementation
204+
class SomeService : Service() {
205+
206+
var bound = false
207+
208+
override fun onBind(intent: Intent?): IBinder? {
209+
bound = true
210+
return null
211+
}
212+
213+
override fun onUnbind(intent: Intent?): Boolean {
214+
bound = false
215+
return super.onUnbind(intent)
216+
}
217+
218+
//more methods
219+
}
220+
{% endhighlight %}
221+
222+
## Shadow
223+
//TODO
File renamed without changes.

0 commit comments

Comments
 (0)