Mais conteúdo relacionado Semelhante a Uma abordagem de testes instrumentados usando MockK, Koin e Robot Pattern (20) Uma abordagem de testes instrumentados usando MockK, Koin e Robot Pattern1. 1
Uma abordagem de testes
instrumentados usando MockK, Koin
e Robot Pattern
Lucas Conceição
© 2019 ThoughtWorks
2. Lucas Conceição
Senior Android consultant
11 anos codando
4 anos codando para Android
2 anos codando em Kotlin
7 meses na ThoughtWorks
2© 2019 ThoughtWorks
4. O quê, por quê e como
Testes instrumentados
4© 2019 ThoughtWorks
6. São feitos com
Espresso
Biblioteca para interação com
interfaces.
@Test
fun checkTheAnswer_toLife_theUniverse_andEverything() {
onView(withText("4")).perform(click())
onView(withText("0")).perform(click())
onView(withText("+")).perform(click())
onView(withText("2")).perform(click())
onView(withText("=")).perform(click())
onView(withId(R.id.result))
.check(matches(withText("42")))
}
6© 2019 ThoughtWorks
9. Motivação
Testes instrumentados:
-Envolvem código de camadas diferentes do app
-Possuem um custo computacional alto
-Podem ser visto/utilizado por pessoas que não necessariamente têm contexto de
Android.
E por que falar disso?
9© 2019 ThoughtWorks
10. Separe o "o quê" do "como"
Robot Pattern
10© 2019 ThoughtWorks
11. “We're programmers, we are lazy. We want the
most efficient thing that minimizes the amount of
work that we have to do.”
- Clarice Lispector
11© 2019 ThoughtWorks
12. “We're programmers, we are lazy. We want the
most efficient thing that minimizes the amount of
work that we have to do.”
- Clarice Lispector Jake Wharton
12© 2019 ThoughtWorks
13. // O fluxo que precisa ser testado
@Test
fun
checkTheAnswer_toLife_theUniverse_andEverything() {
typeText("40")
sumWith()
typeText("2")
askResult()
checkResultIs("42")
}
// Como testar esse fluxo
fun typeText(amount: String) {
for (index in amount.indices) {
onView(withText(amount[index]))
.perform(click())
}
}
fun sumWith() =
onView(withText("+")).perform(click())
fun askResult() =
onView(withText("=")).perform(click())
fun checkResultIs(result: String) =
onView(withId(R.id.result))
.check(matches(withText(result)))
13© 2019 ThoughtWorks
14. Dá pra melhorar
// O fluxo que precisa ser testado
@Test
fun checkTheAnswer_toLife_theUniverse_andEverything() {
openCalculator {
typeText("40")
sumWith()
typeText("2")
} askResult {
checkResultIs("42")
}
}
Aproveitando as language features do Kotlin dá pra ficar de um jeito mais legível
14© 2019 ThoughtWorks
16. MockK
@Test
fun whenLightIsGreen_driverBehavesAccordingly() {
val trafficLight = TrafficLight().apply { color = Color.RED }
val mockedCar: Car = mockk()
every { mockedCar.honk() } returns Unit
val driver = Driver()
driver.enterCar(mockedCar)
driver.observe(trafficLight)
trafficLight.notify(Color.GREEN)
verify { mockedCar.honk() }
}
Como usar
16© 2019 ThoughtWorks
17. MockK
@Test
fun suvDrivers_areKindOfJerks() {
// inicializa trafficLight e mockedCar
val honkIntensity = slot<Int>()
every { mockedCar.type } returns CarType.SUV
every { mockedCar.honk(capture(honkIntensity)) } returns Unit
// driver é criado, entra no mockedCar e observa a trafficLight
trafficLight.notify(Color.GREEN)
assertThat(honkIntensity.isCaptured).isTrue()
assertThat(honkIntensity.captured).isAtLeast(92)
}
Teste os argumentos passados utilizando Slots
17© 2019 ThoughtWorks
19. Injeção de dependência
// A própria classe cria suas dependências
class CarViewModel : ViewModel() {
private val repository: CarRepository
constructor() {
repository = CarRepository()
}
}
19© 2019 ThoughtWorks
// Quem vai usar o objeto decide
// o que passar para ele
class CarViewModel : ViewModel() {
private val repository: CarRepository
constructor(repository: CarRepository) {
this.repository = repository
}
}
20. Injeção de dependência
class CarViewModel : ViewModel(), KoinComponent {
private val repository: CarRepository by inject()
fun loremIpsum() {
repository.someMethod()
}
}
Com Koin
20© 2019 ThoughtWorks
class CarViewModel : ViewModel(), KoinComponent {
fun onlyMethodThatNeedsTheRepositoryInstance() {
val repository: CarRepository = get()
repository.someMethod()
}
}
21. Koin
val storageModule = module {
single {
Room.databaseBuilder(
get(),
AppDatabase::class.java,
"your-database"
).build()
}
}
val appModule = module {
single { CarLocalDataSource(get()) }
factory { CarRepository(get()) }
}
Como configurar
21© 2019 ThoughtWorks
// Na sua classe Application
startKoin {
modules(listOf(
appModule,
storageModule /*, networkModule etc. */
))
}