You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`Testy intregracyjne` są przeprowadzane w celu wykrycia błędów zachodzących w interakcji pomiędzy integrowanymi interfejsami systemu i polegają na testowaniu reakcji na wywołane zdarzenia oraz wymianie danych między testowanymi elementami. Wykonywane są na wielu poziomach testowania i dotyczą całego obszaru integracji (funkcjonalności, moduły, systemy). Realizacją testów integracyjnych w Android są `testy UI` (`testy interfejsu użytkownika`) polegające na przeprowadzeniu zestawu ciągu operacji w docelowej aplikacji z punktu widzenia użytkownika. `Ręczne testy` przeprowadzane przez testera choć ważne są jednak obciążone sporym nakładem czasowym i podatne na błędy, dlatego warto dążyć do automatyzacji testów UI. Instrumentalne `automatyczne testy` interfejsu użytkownika mogą dotyczyć jednej aplikacji lub interakcji między aplikacjami czy też aplikacją i systemem. Przykładem biblioteki przeznaczonej do testów UI obejmujących jedną aplikację jest `Espresso`, a dla interakcji między aplikacjami i systemem np. `UI Automator`.
15
+
16
+
## Charakterystyka
17
+
Testy jednej aplikacji weryfikują zachowanie docelowej aplikacji w stosunku do przeprowadzonych ustalonych działań lub wprowadzenia danych w ekranie aplikacji (`Aktywności`) przez użytkownika. Espresso w sposób automatyczny umożliwia przeprowadzanie symulacji akcji użytkownika w aplikacji oraz sprawdzenie oczekiwanego rezultatu po stronie UI co może przełożyć się na zwiększenie jakości `User Experience`. Udostępnione podstawowe API jest niewielkie, proste, intuicyjne i działa w oparciu o interakcję oraz asercje stanów widoków. Przeznaczone jest do operowania na konkretnych widokach oraz wybranych elementach kolekcji widoków. Espresso zapewnia właściwe zarządzanie wątkiem głównym dzięki czemu uruchamia polecenia testowe we właściwym czasie co zwalnia programistę z obowiązku tworzenia tymczasowych obejść.
18
+
19
+
>**Przykład**
20
+
>Na podstawie Aktywności `MainActivity` zostaną przedstawione możliwości implementacji automatycznych testów UI w Espresso.
21
+
22
+
{% highlight kotlin %}
23
+
class MainActivity : AppCompatActivity() {
24
+
25
+
companion object {
26
+
const val NAME = "NAME"
27
+
}
28
+
29
+
override fun onCreate(savedInstanceState: Bundle?) {
30
+
super.onCreate(savedInstanceState)
31
+
setContentView(R.layout.activity_main)
32
+
33
+
buttonAction.setOnClickListener {
34
+
val text = editTextName.text.toString()
35
+
if(text.equals("hide"))
36
+
textViewName.visibility = INVISIBLE
37
+
else {
38
+
textViewName.visibility = VISIBLE
39
+
textViewName.text = text
40
+
}
41
+
}
42
+
43
+
buttonNavigate.setOnClickListener {
44
+
val intent = Intent(this, SecondActivity::class.java)
//ListView with dummy adapter can be replaced by some RecyclerView
54
+
val items: List<String> = listOf("name1", "name2", "name3")
55
+
val adapter: ArrayAdapter<String> = ArrayAdapter(this, android.R.layout.simple_list_item_1, items)
56
+
listViewNames.adapter = adapter
57
+
listViewNames.setOnItemClickListener { parent, view, position, id -> textViewName.text = adapter.getItem(position)}
58
+
}
59
+
}
60
+
{% endhighlight %}
61
+
62
+
## Metoda testowa
63
+
Wykorzystanie zasady `ActivityTestRule` w klasie testowej sprawia, że aktywność jest uruchamiana przed startem każdego testu i zamykana po jego zakończeniu, redukując tym samym ilość powtarzającego się kodu co w przypadku testów Espresso jest znacznym uproszczeniem. Metoda testowa w Espresso przeprowadza operacje na widokach aktywności. Najpierw uzyskuje dostęp do komponentu interfejsu graficznego, następnie symuluje interakcje użytkownika z widokiem i na końcu weryfikuje jego stan z oczekiwanym. Aby jeszcze bardziej uprościć tworzenie testów warto wykorzystać `Hamcrest`, który oferuję elastyczną składnie zapytań.
64
+
65
+
{% highlight kotlin %}
66
+
@RunWith(AndroidJUnit4::class)
67
+
class EspressoTest {
68
+
69
+
@get:Rule
70
+
var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
Aby znaleźć widok należy wywołać `onView` lub dla kolekcji widoków `onData` oraz przekazać zapytanie (`matcher`) wskazujące na oczekiwany widok np. na podstawie id (`withId`), zawartości (`withText`, `containsString`) czy typu (`instanceOf`). W rezultacie zostanie zwrócony obiekt typu `ViewInteraction` lub `DataInteraction` (dla kolekcji widoków), który umożliwia przeprowadzenie interakcji z widokiem. Należy mieć jednak na uwadze, że Android nie gwarantuje unikatowych ID elementów co w przypadku sytuacji używania tego samego ID przez kilka różnych widoków może powodować problemy (wyrzuci wyjątek `AmbiguousViewMatcherException`). W takich sytuacjach warto wykorzystywać kombinacje dopasowań za pomocą metody `allOf`. W przypadku kolekcji widoków takich jak np. `ListView` nie ma pewności uzyskania żądanego widoku za pomocą `onView` (tylko część elementów jest widoczna) dlatego dostęp powinien odbywać się poprzez dane za pomocą `onData`.
88
+
89
+
{% highlight kotlin %}
90
+
@RunWith(AndroidJUnit4::class)
91
+
class AccessViewTest {
92
+
93
+
@get:Rule
94
+
var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
95
+
96
+
@Test
97
+
fun changeTextFromViewAccess() {
98
+
onView(allOf(withId(R.id.editTextName), instanceOf(EditText::class.java))) //combine id with type
99
+
.perform(typeText("value")) //do action
100
+
101
+
onView(withText("action")) //only one matcher
102
+
.perform(click())
103
+
104
+
onView(withId(R.id.textViewName)) //only one matcher
105
+
.check(matches(withText("value")))
106
+
}
107
+
108
+
@Test
109
+
fun changeTextFromAdapterViewAccess() {
110
+
onData(anything())
111
+
.inAdapterView(withId(R.id.listViewNames))
112
+
.atPosition(0)
113
+
.perform(click())
114
+
115
+
onView(withId(R.id.textViewName))
116
+
.check(matches(withText("name1")))
117
+
}
118
+
}
119
+
{% endhighlight %}
120
+
121
+
## Przeprowadzenie akcji
122
+
Uruchomienie akcji następuje poprzez wyołanie metody `perform` klasy `ViewInteraction` lub `DataInteraction` w zależności od rodzaju elementu oraz przekazanie obiektów typu `ViewAction` jako argumentów. Akcje jakie można wykonać to m.in. `click`, `typeText`, `scrollTo`, `pressKey`, `clearText`. Ponadto jeśli podejmowana akcja dotyczy intencji (`Intent`) można wykorzystać zasadę `IntentsTestRule`, która waliduje intencje wysłane przez testowane aplikację oraz metody weryfikujące `intended` dla `startActivity` i `intending` dla `startActivityForResult`.
123
+
124
+
{% highlight kotlin %}
125
+
@RunWith(AndroidJUnit4::class)
126
+
class ActionViewTest {
127
+
128
+
@get:Rule
129
+
var intentsRule: IntentsTestRule<MainActivity> = IntentsTestRule(MainActivity::class.java)
Sprawdzanie stanu widoków następuje poprzez wyołanie metody `check` klasy `ViewInteraction` lub `DataInteraction` w zależności od rodzaju elementu oraz przekazanie obiektów typu `ViewAssertion` jako argumentów. Asercje jakie można wykonać to m.in. `doesNotExist`, `matches`, `selectedDescendentsMatch`.
146
+
147
+
{% highlight kotlin %}
148
+
@RunWith(AndroidJUnit4::class)
149
+
class EspressoTest {
150
+
151
+
@get:Rule
152
+
var activityRule: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java)
0 commit comments