Bob Dahlberg
Team Lead Mobile @ Isotop
Who’s Bob?
- Building software
- Kotlin since 2015
- Testing since Flash
Slide 3
Slide 3 text
Somewhat painful testing
Slide 4
Slide 4 text
Somewhat
Painful
Testing
UI Testing - Use Espresso
Slide 5
Slide 5 text
Somewhat
Painful
Testing
UI Testing - Use Espresso
Instrumentation Testing
“You should create instrumented unit
tests if your tests need access to
instrumentation information (such as
the target app’s context) or if they
require the real implementation of an
Android framework component (such as
a Parcelable or SharedPreferences obj
ect).”
Slide 6
Slide 6 text
Somewhat
Painful
Testing
UI Testing - Use Espresso
Instrumentation Testing
“You should create instrumented unit
tests if your tests need access to
instrumentation information (such as
the target app’s context) or if they
require the real implementation of an
Android framework component (such as
a Parcelable or SharedPreferences obj
ect).”
Slide 7
Slide 7 text
Somewhat
Painful
Testing
UI Testing - Use Espresso
Instrumentation Testing
@RunWith(AndroidJUnit4::class)
@SmallTest
class InstrumentedExampleTest {
@Test
fun use_app_context() {
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals(“se.accepted.test”, appContext.packageName)
}
}
Slide 8
Slide 8 text
Somewhat
Painful
Testing
UI Testing - Use Espresso
Instrumentation Testing
- Databases
- Real Permissions
- Build version specific on device
- Black box testing
- Learning
@RunWith(AndroidJUnit4::class)
@SmallTest
class InstrumentedExampleTest {
@Test
fun use_app_context() {
val appContext = InstrumentationRegistry.getTargetContext()
assertEquals(“se.accepted.test”, appContext.packageName)
}
}
Slide 9
Slide 9 text
Unit Testing!
Slide 10
Slide 10 text
Unit
Testing
The simplest unit
class ExampleUnitTest {
@Test
fun addition_is_correct() {
assertEquals(4, 2+2)
}
}
Slide 11
Slide 11 text
Unit
Testing
The simplest unit
class ExampleUnitTest {
@Test
fun addition_is_correct() {
assertEquals(4, 2+2)
}
@Test
fun addition_on_person() {
val bob = Person(age=33)
val oldBob = bob+1
assertEquals(34, oldBob.age)
}
}
Slide 12
Slide 12 text
Unit
Testing
The simplest unit
JUnit4 - assert
class UnitTest {
@Test
fun startsWith() {
val uri = "https://accepted.se"
assertEquals(uri.substring(0,4), "http")
}
}
Slide 13
Slide 13 text
Unit
Testing
The simplest unit
JUnit4 - assert
class UnitTest {
@Test
fun startsWith() {
val uri = "https://accepted.se"
assertEquals(uri.substring(0,5), "https")
}
}
Slide 14
Slide 14 text
Unit
Testing
The simplest unit
JUnit4 - assert
class UnitTest {
@Test
fun startsWith() {
val uri = "https://accepted.se"
assertEquals(uri.substring(0,5), "https")
assertEquals(uri.indexOf("https"), 0)
}
}
Slide 15
Slide 15 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
class HamkrestUnitTest {
@Test
fun startsWith() {
val uri = "https://accepted.se"
assert.that(uri, startsWith(“https”))
}
}
Slide 16
Slide 16 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
class HamkrestUnitTest {
@Test
fun startsWith() {
val uri = "https://accepted.se"
assert.that(uri, startsWith(“https") and endsWith(“.se”))
}
}
Slide 17
Slide 17 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
class HamkrestUnitTest {
@Test
fun startsWith() {
val hasOnlyOneColon = Matcher(String::singleChar,’:’)
val uri = "https://accepted.se"
assert.that(uri, startsWith(“https") and hasOnlyOneColon)
}
}
fun String.singleChar(c:Char):Boolean = count{ it == c } == 1
Slide 18
Slide 18 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
Mockito
@RunWith(MockitoJUnitRunner::class)
class MockitoUnitTest {
@Mock
private lateinit var header:IText
@Test
fun presenterSetsHeader() {
val response = ApiResponse(title=“my header”)
val presenter = Presenter()
presenter.attachView(header)
presenter.onData(response)
verify(header).setText(“my header”)
}
}
Slide 19
Slide 19 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
Mockito
class MockitoUnitTest {
@Mock
private lateinit var header:IText
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
}
@Test
fun presenterSetsHeader() {
val response = ApiResponse(title=“my header”)
val presenter = Presenter()
presenter.attachView(header)
presenter.onData(response)
verify(header).setText(“my header”)
}
}
Slide 20
Slide 20 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
Mockito
Mock everything
@RunWith(MockitoJUnitRunner::class)
class MockitoUnitTest {
@Mock
private lateinit var context:Context
val myText = "Any requested string"
@Before
fun setUp() {
whenever(context.getString(any())).thenReturn(myText)
}
@Test
fun presenterSetsHeader() {
val fromResource = context.getString(R.string.my_string)
assertEquals(myText, fromResource)
}
}
Slide 21
Slide 21 text
Unit
Testing
The simplest unit
JUnit4 - assert
Hamkrest
Mockito
Mock everything
@RunWith(MockitoJUnitRunner::class)
class MockitoUnitTest {
@Test
fun inlineMocking() {
val text = mock {
on { text } doReturn “text”
on { elevation } doReturn 0.4f
}
val reverser = TextReverser(text)
assert.that(reverser.text, equalTo("text".reversed()))
}
}
Slide 22
Slide 22 text
Spek - from Specification
Slide 23
Slide 23 text
Spek
Another flavor
Requires JUnit5
Android standard is JUnit4
Android Studio plugin (recommended)
Writing specifications
Given - When - Then
Slide 24
Slide 24 text
Spek
Another flavor
given - on - it
class ExampleSpek : Spek({
given(“a father”) {
val bob = Person(“Bob”,33)
on(“Sunday morning”){
bob.time = SundayMorning()
it(“should be awake”){
assertThat(bob.awake, true)
}
it(“should play with his child”){
assertThat(bob.isPlaying, true)
}
it(“should be tired”){
assertThat(bob.tired, true)
}
}
}
})
Slide 25
Slide 25 text
Spek
Another flavor
given - on - it
describe - it
class ExampleSpek : Spek({
describe(“a stream from a channel”) {
val stream = Channel("http://stream.dash").asStream()
it(“is not null”){
assertThat(stream, anything)
}
it(“has an uri”){
assertThat(stream.uri, !isEmptyString)
}
it(“has a query”){
assertThat(stream.uri, containsSubstring(“?"))
}
}
})
Slide 26
Slide 26 text
Spek
Another flavor
given - on - it
describe - it
Output
Slide 27
Slide 27 text
Even more flavors
Slide 28
Slide 28 text
More is More
Expekt
23.should.equal(23)
“Kotlin".should.not.contain("Scala")
listOf(1, 2, 3).should.have.size.above(1)
Slide 29
Slide 29 text
More is More
Expekt
Kluent
“Kotlin" shouldEqual “Kotlin"
“Kotlin” shouldNotEqual “Scala"
“Kotlin” `should not equal` "Scala"
When calling stub.getPerson() itReturns bob
Slide 30
Slide 30 text
More is More
Expekt
Kluent
Rx2
Flowable.range(1, 5)
.test()
.assertResult(1, 2, 3, 4, 5)
Slide 31
Slide 31 text
More is More
Expekt
Kluent
Rx2
Flowable.range(1, 5)
.test(0)
.assertValues()
.requestMore(1)
.assertValues(1)
.requestMore(2)
.assertValues(1, 2, 3)
.requestMore(2)
.assertResult(1, 2, 3, 4, 5)