Skip to content

Commit 985326a

Browse files
committed
junit post updated
1 parent 62a6bbd commit 985326a

File tree

5 files changed

+189
-14
lines changed

5 files changed

+189
-14
lines changed

_drafts/2019-01-07-testy_jednostkowe.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Projektowanie przypadków testowych oparciu o `klasy równoważności` polega na
113113
Testowanie `przejść między stanami` analizuje dozwolone i niedozwolone przejścia między stanami aplikacji za pomocą `automatu skończenie stanowego`. Analizując diagram lub tabelę stanową można zaprojektować testy w taki sposób by pokryły wszystkie przejścia, konkretny ciąg czy testowały przejścia zabronione.
114114

115115
>Diagram stanów wraz z możliwymi przejściami dla kosztów dostawy ukazany jest poniżej.
116-
![Diagram stanów](/assets/img/diagrams/testing/flow_states.svg){: .center-image }
116+
![Diagram stanów](/assets/img/diagrams/testing/unit_test_flow_states.svg){: .center-image }
117117

118118
Testowanie z użyciem `tabeli decyzyjnych` polega na sprawdzeniu działania jednostki testowanej w odniesieniu do kombinacji warunków wejściowych znajdujących się w tabeli.
119119

_drafts/2019-01-14-junit.md

Lines changed: 188 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,223 @@ date: 2019-01-14
55
categories: ["Testowanie"]
66
image: testing/junit
77
description: "JUnit"
8-
keywords: "testowanie, testing, testy, jednostkowe, automatyczne, lokalne, zaślepka, atrapa, unit test, mock, stub, przypadki testowe, pokrycie kodu, code coverage, przypadki testowe, junit, roboelectric, mockito, android, programowanie, programming"
8+
keywords: "testowanie, testing, testy, jednostkowe, automatyczne, lokalne, before, after, rule, zaślepka, atrapa, unit test, mock, stub, przypadki testowe, pokrycie kodu, code coverage, przypadki testowe, klasa testowa, metoda testowa, junit, roboelectric, mockito, android, programowanie, programming"
99
---
1010

1111
## Integracja
1212
`Android Studio` ułatwia proces testowania dzięki integracji z bibliotekami wspomagającymi testowanie oraz dostarczeniu dedykowanych narzędzi. Dodając odpowiednie zależności `AndroidX` do projektu już za pomocą kilku kliknięć można wykorzystać możliwości takich bibliotek jaki: `JUnit`, `Mockito`, `Espresso`, `Robolectric` czy `UI Automator`. Lokalne testy jednostkowe uruchamiane na maszynie lokalnej `JVM` znajdują się w lokalizacji: `moduleName/src/test/java/` natomiast instrumentalne testy jednostkowe przeznaczone do uruchamiania na urządzeniu lub emulatorze znajdują się w: `moduleName/src/androidTest/java/`. W przypadku testów instrumentalnych przeznaczonych dla różnych wariantów budowania aplikacji ścieżka zmienia się na: `moduleName/src/androidTestBuildVariantName/java/`.
1313

14-
//TODO photo
14+
![Rysunek struktury folderu](/assets/img/diagrams/testing/junit_folders.png){: .center-image }
1515

1616
## Tworzenie i uruchamianie
1717
`Testy jednostkowe` można tworzyć dodając klasy do odpowiednich folderów lub za pomocą skrótu `Ctrl+Shift+T` kierując kursor na klasę lub metodę. Uruchamianie i analiza następuje z poziomu narzędzia GUI lub konsoli w Android Studio, które umożliwia m.in. eksport wyników testów, przeglądanie statystyk oraz pokrycie kodu.
1818

19-
//TODO photo
19+
![Rysunek pokrycia kodu](/assets/img/diagrams/testing/junit_coverage.png){: .center-image }
2020

2121
## Motywacja
2222
Podstawą procesu testowania jest tworzenie i wykonywanie testów jednostkowych, które odpowiednio napisane w łatwy i wiarygodny sposób weryfikują poprawności logiki jednostki testowanej. Wykonywanie testów jednostkowych przy każdym przyroście ułatwia szybkie wyłapanie błędu. Należy jednak pamiętać o izolacji testowanej jednostki od pozostałych zależności. `JUnit` w Android jest wykorzystywany przede wszystkim do pisania `lokalnych testów` jednostkowych lub prostych `testów instrumentalnych` dla których można dostarczyć zależności (własne lub przy pomocy biblioteki np. Mockito). W pozostałych sytuacjach należy wykorzystać bibliotekę dostarczają zależności środowiska uruchomieniowego np. `Robolectric`. Tworzenie asercji może zostać usprawnione przez wykorzystanie `Hamcrest`. Alternatywą dla implementacji JUnit dla Android jest wykorzystanie biblioteki `Truth` we współpracy z asercjami Android.
2323

2424
## Dobre praktyki
25-
Tworząc metody testowe należy przede wszystkim pamiętać o wykluczeniu wszelkich zależności w taki sposób, aby na wynik testu testowanej jednostki nie miały wpływu inne zależności. Klasy testowe powinny znajdować się w pakiecie o tej samej nazwie co klasy implementacji, a nazwy klas testowych powinny być podobne do klas testowanych. Metody opisowe powinny być nazywane w sposób opisowy i jednoznaczny w nawiązaniu do celu testu nawet jeśli z tego powodu nazwa metody jest długa. W tym celu można posłużyć się konwencją `BDD: Given/When/Then`. Ponadto należy dążyć do minimalizacji asercji, czasu wykonywania testów oraz zwiększać pokrycie kodu. Testy powinny być krótkie, proste i ściśle dotyczyć jednej jednostki. Jeśli sytuacja tego wymaga należy wykorzystywać metody cyklu życia testów, aby zapewnić odpowiednią inicjalizację i czyszczenie środowiska.
25+
Tworząc metody testowe należy przede wszystkim pamiętać o wykluczeniu wszelkich zależności w taki sposób, aby na wynik testu testowanej jednostki nie miały wpływu inne zależności. Klasy testowe powinny znajdować się w pakiecie o tej samej nazwie co klasy implementacji, a nazwy klas testowych powinny być podobne do klas testowanych. Metody opisowe powinny być nazywane w sposób opisowy i jednoznaczny w nawiązaniu do celu testu nawet jeśli z tego powodu nazwa metody jest długa. W tym celu można posłużyć się konwencją `Given/When/Then`, gdzie `Given` określa warunki początkowe, `When` opisuje akcje, a `Then` informuje o oczekiwanym rezultacie. Ponadto należy dążyć do minimalizacji asercji, czasu wykonywania testów oraz zwiększać pokrycie kodu. Testy powinny być krótkie, proste i ściśle dotyczyć jednej jednostki. Jeśli sytuacja tego wymaga należy wykorzystywać metody cyklu życia testów, aby zapewnić odpowiednią inicjalizację i czyszczenie środowiska.
2626

2727
## Klasa testowa
2828
Testy w JUnit (metody) zawierają się w `klasie testowej`, które z kolei mogą być częścią `zestawu klas testowych`. Aby klasa była klasą testową w JUnit4 musi zawierać deklarację przynajmniej jednej metody testowej oznaczonej adnotacją `@Test`. Podstawowym elementem testów są asercje sprawdzające wartość logiczną, równość wartości czy referencji.
2929

30-
//TODO klasa z jedna metoda i roznymi asercjami
30+
{% highlight kotlin %}
31+
class SmallTest {
32+
33+
@Test
34+
fun startGameWithDifferentTeamsSize() {
35+
val game = Game()
36+
game.addPlayer(Game.Team.RED, "Johnnie")
37+
game.addPlayer(Game.Team.BLUE, "Jack")
38+
game.addPlayer(Game.Team.BLUE, "Jim")
39+
game.start()
40+
assertFalse(game.hasStarted())
41+
}
42+
}
43+
{% endhighlight %}
3144

3245
## Cykl życia
3346
Klasy testowe poza metodami testowymi mogą składać się także z metod inicjalizacyjnych i końcowych. Metoda oznaczona adnotacją `@Before` wykonywana jest przed każdym testem i służy przygotowaniu środowiska testowego, natomiast z adnotacją `@After` po każdym teście co wykorzystywane jest do czyszczenia środowiska. W analogiczny sposób działają metody oznaczone jako `@BeforeClass` oraz `@AfterClass`, które wykonują się kolejno przed i po uruchomieniu wszystkich testów. Jeśli metoda testowa ma zostać wyłączona z testów należy użyć adnotacji `@Ignore`. Adnotacja `@Test` może zostać wzbogacona o maksymalny czas wykonania (`timeout`) lub oczekiwany typ wyjątku.
3447

35-
//TODO klasa z calym cyklem i ignore i test
48+
{% highlight kotlin %}
49+
class FullTest {
50+
51+
//mock some dependencies
52+
val game = Game()
53+
54+
@Before
55+
fun init() {
56+
//clear game before every test
57+
game.clearTeams()
58+
}
59+
60+
@After
61+
fun uninit() {
62+
//clear game before every test
63+
game.stop()
64+
}
65+
//use @BeforeClass, @AfterClass in the same way
66+
67+
@Ignore //ignore this test temporary
68+
fun startGameWithDifferentTeamsSize() {
69+
game.addPlayer(Game.Team.RED, "Johnnie")
70+
game.addPlayer(Game.Team.BLUE, "Jack")
71+
game.addPlayer(Game.Team.BLUE, "Jim")
72+
game.start()
73+
assertFalse(game.hasStarted())
74+
}
75+
76+
@Test(timeout = 1000) //after 1000ms when test couldn't finished than just failed
77+
fun startAndStopGame() {
78+
game.addPlayer(Game.Team.RED, "Johnnie")
79+
game.addPlayer(Game.Team.RED, "William")
80+
game.addPlayer(Game.Team.BLUE, "Jack")
81+
game.addPlayer(Game.Team.BLUE, "Jim")
82+
game.start()
83+
assertTrue(game.hasStarted())
84+
game.stop()
85+
assertFalse(game.hasStarted())
86+
}
87+
88+
@Test
89+
fun modifyTeamsAndStartGame() {
90+
game.addPlayer(Game.Team.RED, "Johnnie")
91+
game.addPlayer(Game.Team.RED, "William")
92+
game.addPlayer(Game.Team.BLUE, "Jack")
93+
game.start()
94+
assertFalse(game.hasStarted())
95+
game.removePlayer("William")
96+
game.start()
97+
assertTrue(game.hasStarted())
98+
}
99+
}
100+
{% endhighlight %}
36101

37102
## Parametry
38-
Klasa testowa może posiadać także jedną metodę generującą zestawy danych dla metod testowych. Klasa ta musi być oznaczona adnotacją `@RunWith(Parameterized.class)` natomiast metoda zwracająca kolekcje danych oraz pola przyjmujące wstrzyknięte wartości są oznaczone jako `@Parameter`. Pola mogą przyjmować wstrzykniętą wartość również za pomocą konstruktora.
39-
40-
//TODO przyklad z parametrami
103+
Klasa testowa może posiadać także jedną metodę generującą zestawy danych dla metod testowych. Klasa ta musi być oznaczona adnotacją `@RunWith(Parameterized.class)` natomiast metoda statyczna zwracająca kolekcje danych oznaczone jako `@Parameters`. Właściwości mogą przyjmować wstrzykniętą wartość za pomocą konstruktora lub być zdefiniowane w ciele klasy i oznaczone jako @Parameter(number).
104+
105+
{% highlight kotlin %}
106+
@RunWith(Parameterized::class)
107+
class ParameterizedTest(val redGoals: Int, val blueGoals: Int, val scores: String) {
108+
109+
companion object {
110+
val game = Game()
111+
112+
@BeforeClass @JvmStatic
113+
fun initTeams() {
114+
game.addPlayer(Game.Team.RED, "Johnnie")
115+
game.addPlayer(Game.Team.RED, "William")
116+
game.addPlayer(Game.Team.BLUE, "Jack")
117+
game.addPlayer(Game.Team.BLUE, "Jim")
118+
}
119+
120+
@AfterClass @JvmStatic
121+
fun uninitTeams() {
122+
game.clearTeams()
123+
}
124+
125+
//create test data
126+
@Parameters @JvmStatic
127+
fun createData(): Collection<Array<Any>> {
128+
return listOf(
129+
arrayOf(0, 2, "RED 0:2 BLUE"),
130+
arrayOf(10, 5, "RED 10:5 BLUE"),
131+
arrayOf(3, 3, "RED 3:3 BLUE"))
132+
}
133+
}
134+
135+
//use auto parameterized test method
136+
@Test
137+
fun scoreAndCheckResultWithParameterized() {
138+
game.start()
139+
repeat(redGoals) { game.goal(Game.Team.RED) }
140+
repeat(blueGoals) { game.goal(Game.Team.BLUE) }
141+
assertEquals(scores, game.getScore())
142+
game.stop()
143+
}
144+
145+
//instead of manual parameterized
146+
@Test
147+
fun scoreAndCheckResultNormal() {
148+
//first test
149+
game.start()
150+
repeat(2) { game.goal(Game.Team.BLUE) }
151+
assertEquals("RED 0:2 BLUE", game.getScore())
152+
game.stop()
153+
154+
//second test
155+
game.start()
156+
repeat(10) { game.goal(Game.Team.RED) }
157+
repeat(5) { game.goal(Game.Team.BLUE) }
158+
assertEquals("RED 10:5 BLUE", game.getScore())
159+
game.stop()
160+
161+
//third test
162+
game.start()
163+
repeat(3) { game.goal(Game.Team.RED) }
164+
repeat(3) { game.goal(Game.Team.BLUE) }
165+
assertEquals("RED 3:3 BLUE", game.getScore())
166+
game.stop()
167+
}
168+
}
169+
{% endhighlight %}
41170

42171
## Zasady
43172
JUnit umożliwia dodawanie zachowania do każdego testu za pomocą adnotacji `@Rule` oraz tworzenie nowych zasad. Aby stworzyć własną zasadę należy w klasie zasady implementować interfejs `TestRule`.
44173

45-
//TODO przyklad z tworzeniem i uzyciem wlasnej zasady
174+
{% highlight kotlin %}
175+
class RuleTest {
176+
177+
@Rule @JvmField
178+
val rule = PrintTestRule() //this rule print message before and after every test
179+
180+
val game = Game()
181+
182+
@Test
183+
fun addPlayersWithSameNameToTheSameTeam() {
184+
game.addPlayer(Game.Team.RED, "Johnnie")
185+
game.addPlayer(Game.Team.RED, "William")
186+
game.addPlayer(Game.Team.RED, "Jack")
187+
game.addPlayer(Game.Team.RED, "William")
188+
assertEquals(3, game.getRedTeam().size)
189+
}
190+
191+
@Test
192+
fun addPlayersWithSameNameToTheAnotherTeam() {
193+
game.addPlayer(Game.Team.RED, "Johnnie")
194+
game.addPlayer(Game.Team.RED, "William")
195+
game.addPlayer(Game.Team.BLUE, "Jack")
196+
game.addPlayer(Game.Team.BLUE, "William")
197+
assertEquals(1, game.getBlueTeam().size)
198+
}
199+
}
200+
201+
class PrintTestRule : TestRule {
202+
203+
private lateinit var base: Statement
204+
private lateinit var description: Description
205+
206+
override fun apply(base: Statement, description: Description): Statement {
207+
this.base = base
208+
this.description = description
209+
return PrintTestStatement(base)
210+
}
211+
212+
class PrintTestStatement(private val base: Statement) : Statement() {
213+
override fun evaluate() {
214+
println("Log before test action")
215+
base.evaluate()
216+
println("Log after testaction")
217+
}
218+
}
219+
}
220+
{% endhighlight %}
46221

47222
`AndroidX Test` zawiera zestaw gotowych zasad dla JUnit, które zwiększają elastyczność, redukują powtarzający się kod oraz wspomagają testowanie komponentów Android. Wykorzystywane są przede wszystkim testach UI przy użyciu `Espresso`. `ActivityTestRule` dostarcza do klasy testowej żądanej Aktywności (`Activity`), która jest dostępna w całym cyklu życia klasy testowej, `ServiceTestRule` dostarcza Usługę (`Service`) natomiast `IntentsTestRule` dostarcza Intencję (`Intent`).
48223

49-
## Filtry
50-
`AndroidJUnitRunner` umożliwia stosowanie adnotacji w celach czysto informacyjnych. Adnotacja `@RequiresDevice` mówi, że test powinien zostać przeprowadzony tylko na urządzeniu fizycznym, `@SdkSupress` określa minimalne API natomiast `@SmallTest`, `@MediumTest` i `@LargeTest` mówią o wielkości testu co przekłada się na czas jego wykonania.
224+
//TODO maybe
51225

52-
//TODO example
226+
## Filtry
227+
`AndroidJUnitRunner` umożliwia stosowanie adnotacji dla testów instrumentalnych w celach informacyjnych. Adnotacja `@RequiresDevice` mówi, że test powinien zostać przeprowadzony tylko na urządzeniu fizycznym, `@SdkSupress` określa minimalne API natomiast `@SmallTest`, `@MediumTest` i `@LargeTest` informują o wielkości testu co przekłada się na czas jego wykonania.
15.8 KB
Loading
9.75 KB
Loading
File renamed without changes.

0 commit comments

Comments
 (0)