Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kodein DI & KotlinTest

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →

Kodein DI & KotlinTest

Other Decks in Technology

Transcript

  1. • Dependency retrieval container • Funciona en JVM, Android, JS,

    y plataformas nativas (como iOS) • Uso extensivo de inline y Kotlin DSL • Versión 5.0.0 estable Introducción a Kodein-DI
  2. val kodein = Kodein { bind<Random>() with singleton { Random()

    } }
 
 class MyKodeinClient(private val kodein: Kodein) { private val random: Random by kodein.instance() } Un ejemplo ‘hello world’
  3. val kodein = Kodein { bind<Random>() with singleton { Random()

    } }
 
 class MyKodeinClient(private val kodein: Kodein) { private val random: Random by kodein.instance() } Un ejemplo básico
  4. val randomModule = Kodein.Module { bind<Random>() with singleton { Random()

    } }
 
 fun retrofitModule(baseUrl: String) = Kodein.Module { bind<Retrofit>() with singleton { Retrofit.Builder() .baseUrl(baseUrl) .build() } } Módulos en Kodein Pueden depender unos de otros. No hay que preocuparse del orden de inicialización.
  5. class App : Application(), KodeinAware { override val kodein =

    Kodein.lazy { import(androidModule(this@App)) import(retrofitModule(baseUrl) } } // No Android val kodein = Kodein { import(…) import(…) } Uso de módulos
  6. class App : Application(), KodeinAware { override val kodein =

    Kodein.lazy { import(androidModule(this@App)) import(retrofitModule(baseUrl) } } // No Android val kodein = Kodein { import(…) import(…) } Uso de módulos
  7. • Provee todos los SystemService • Directorios de caché •

    SharedPreferences • Resources y assets • ContentResolver, MainLooper, etc.. androidModule
  8. class MyActivity : Activity(), KodeinAware { override val kodein by

    closestKodein() private val random: Random by kodein.instance() } Obtención de las dependencias • closestKodein() funciona directamente en cualquier clase Android. • Se usará closestKodein(context) para clases no Android
  9. val randomModule = Kodein.Module { bind<Random>() with singleton { Random()

    } } val randomDirectModule = Kodein.Module { bind() from singleton { Random() } } Binding tipado vs directo
  10. val module = Kodein.Module { bind<Random>() with singleton { Random()

    } bind<Int>("randomInt") with provider { instance<Random>().nextInt()} constant(“baseUrl") with "https://www.company.com/api" } Tagged bindings / constants
  11. val dataModule = Kodein.Module { bind<OkHttpClient>() with singleton { OkHttpClient.newBuilder().build()

    } bind<Retrofit>() with singleton { Retrofit.Builder() .baseUrl(baseUrl) .client(instance()) .build() } } Dependencias transitivas
  12. val randomModule = Kodein.Module { bind<Random>() with provider { Random()

    } } val retrofitModule = Kodein.Module { bind<Retrofit>() with factory { baseUrl: String -> Retrofit.Builder() .baseUrl(baseUrl) .build() } } Providers y factories
  13. class MyActivity : Activity(), KodeinAware { private val _parentKodein by

    closestKodein() override val kodein: Kodein = Kodein.lazy { extend(_parentKodein) bind() from provider { MainPresenter(this@MainActivity, instance(), instance(), instance()) } } val presenter: MainPresenter by instance() } Submódulos en Android
  14. val randomModule = Kodein.Module { bind<MainPresenter>() with scoped(androidScope<Activity>()).singleton { MainPresenter(…)

    } bind<OtherPresenter>() with scoped(androidRetainedScope<Activity>()) .singleton { OtherPresenter(…) } } Scopes en Android
  15. • Extensión de Kodein DI: Global & ConfigurableKodein. • Permite

    tener diferentes contenedores Kodein. • ConfigurableKodein permite añadir y quitar módulos dinámicamente: uso en tests. • Karumi ofrece un ejemplo en https://github.com/Karumi/ kodein-sample-testing Tests unitarios y de instrumentación
  16. • Ventajas frente a Dagger: • Drástica reducción de la

    verbosidad. • Aprendizaje más fácil para programadores no experimentados en DI. • Inconvenientes: • Se pierde la validación en tiempo de compilación. • Mayor coste de integración en código Java. Kodein vs Dagger
  17. • Kodein ofrece una alternativa a Dagger, rápida y fácil

    de aprender y utilizar. • Dagger sigue siendo sólido y ofrece errores en tiempo de compilación cuando hay inyecciones mal hechas. Conclusiones
  18. @Test fun testTransformAlbums_withValidArtists() { // When val albums = albumMapper.transform(lastFmValidAlbums)

    // Then assertEquals(2, albums.size) assertEquals(lastFmValidAlbums[0].mbid, albums[0].id) assertEquals(lastFmValidAlbums[1].mbid, albums[1].id) } Motivación: tests en jUnit 4
  19. @Test fun testTransformAlbums_withValidArtists() { // When val albums = albumMapper.transform(lastFmValidAlbums)

    // Then assertEquals(2, albums.size) assertEquals(lastFmValidAlbums[0].mbid, albums[0].id) assertEquals(lastFmValidAlbums[1].mbid, albums[1].id) } Motivación: tests en jUnit 4
  20. • Spock • Spek framework • jUnit 5 (estable desde

    septiembre de 2017) Alternativas a jUnit 4
  21. on("transforming two valid albums") { val albums = albumMapper.transform(lastFmValidAlbums) it("should

    get an album list with two items") { assert(albums.size == 2) } it("should get expected first item in albums list") { assert(albums[0].id == lastFmValidAlbums[0].mbid) } it("should get expected second item in albums list") { assert(albums[1].id == lastFmValidAlbums[1].mbid) } } Ejemplo en Spek
  22. class AlbumMapperTest { @Before fun setUp() {…} @Test fun testA()

    {…} @Test fun testB() {…} @Test fun testC() {…} } jUnit 4 vs Spek
  23. class AlbumMapperSpekTest : Spek({ given("some conditions")) { beforeEachTest {…} on(“doing

    something") { // Any action it("should meet assertion A") { // An assertion } it("should meet assertion B") { // Another assertion } } } }) jUnit 4 vs Spek
  24. • Funciona sobre jUnit5 • Integración fácil en Android Studio

    3.2 
 (Canary 4) • Última versión lanzada 3.1.2
 Spek continúa en 1.1.5 KotlinTest
  25. class MyStringSpec : StringSpec({ "strings.length should return size of string"

    { "hello".length shouldBe 5 } }) class MyFunSpec : FunSpec({ test("String.length should return the length of the string") { "sammy".length shouldBe 5 "".length shouldBe 0 } }) class MyShouldSpec : ShouldSpec({ should("return the length of the string") { "sammy".length shouldBe 5 "".length shouldBe 0 } }) Specs básicos
  26. class AlbumMapperTest : BehaviorSpec({ given("some conditions")) { // Before all

    tests When(“doing something") { // Any action Then("should meet assertion A") { // An assertion } Then(“should meet assertion B") { // Another assertion } } } }) BehaviorSpec
  27. class AlbumMapperTest : DescribeSpec({ describe("some conditions")) { // Before all

    tests context(“doing something") { // Any action it("should meet assertion A") { // An assertion } it(“should meet assertion B") { // Another assertion } } } }) DescribeSpec
  28. • Lógicos (true/false) • Strings • Int, Long, Double, Float

    • Colecciones y mapas • … Matchers incluidos en KotlinTest
  29. fun startsWithA() = object : Matcher<String> { override fun test(value:

    String) = Result( value.startsWith("a"), "String $value should start with a", "String $value should not start with a” )} "abc" should startsWithA() "xyz" shouldNot startsWithA() Custom matchers
  30. val strings = listOf("abc", "def", "ghi") forAll(strings) { x ->

    x should haveLength(3) } forAll, forNone, forOne, forAtMostOne, forAny, forExactly(n)… Inspectores
  31. "multiplication table" { val myTable = table( headers("a", "b", "result"),

    row(9, 1, 9), row(9, 2, 18), row(9, 3, 27) ) forAll(myTable) { a, b, result -> a * b shouldBe result } } Table-driven test
  32. class PropertyExample: StringSpec( { "String size" { assertAll({ a: String,

    b: String -> (a + b).length shouldBe a.length + b.length }) } }) • Repeticiones generadas automáticamente • 1000 por defecto Property testing
  33. • KotlinTest provee generadores para los tipos más comunes. •

    Permite crear generadores propios: class MyIntGenerator : Gen<Int> { override fun constants() = listOf(1, 2, 3) override fun random() = generateSequence { Math.random().toInt() } } Property testing
  34. • Tags y grupos por tags • Tests de futuros

    • Extensiones para Arrow • … Y mucho más…
  35. • El uso extensivo de DSL y funciones infix nos

    permite aumentar la legibilidad de los tests. • jUnit 5 (y todos los frameworks basados en él) ofrecen posibilidades mucho más potentes que jUnit 4. Conclusiones