From 525139dbb1500e4f9684c215b865dd1b0b476587 Mon Sep 17 00:00:00 2001 From: Serchinastico <54cymru@gmail.com> Date: Thu, 7 Feb 2019 13:13:28 +0100 Subject: [PATCH] Add tests --- app/build.gradle | 4 +- .../superheroes/ui/view/FragmentTest.kt | 18 +--- .../superheroes/worker/ThanosWorkerTest.kt | 84 +++++++++++++++++++ .../superheroes/SuperHeroesApplication.kt | 15 ++-- .../repository/LocalSuperHeroDataSource.kt | 20 +---- .../repository/RemoteSuperHeroDataSource.kt | 4 +- .../data/repository/room/SuperHeroDao.kt | 17 ++++ .../superheroes/worker/ThanosWorker.kt | 2 - 8 files changed, 122 insertions(+), 42 deletions(-) create mode 100644 app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt diff --git a/app/build.gradle b/app/build.gradle index 382ba0a..1987510 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,7 +58,7 @@ dependencies { implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-beta01" implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-beta01" implementation "androidx.room:room-runtime:2.1.0-alpha04" - implementation "android.arch.work:work-runtime:1.0.0-beta04" + implementation "android.arch.work:work-runtime-ktx:1.0.0-beta05" kapt "androidx.room:room-compiler:2.1.0-alpha04" implementation "androidx.fragment:fragment:1.1.0-alpha03" debugImplementation "androidx.fragment:fragment-testing:1.1.0-alpha03" @@ -83,7 +83,7 @@ dependencies { androidTestImplementation "androidx.test.espresso:espresso-contrib:3.1.1" androidTestImplementation "androidx.test.espresso:espresso-intents:3.1.1" androidTestImplementation "androidx.room:room-testing:2.1.0-alpha04" - androidTestImplementation "android.arch.work:work-testing:1.0.0-beta04" + androidTestImplementation "android.arch.work:work-testing:1.0.0-beta05" } shot { diff --git a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt index a2cd13d..fa6d28f 100644 --- a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt +++ b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt @@ -9,16 +9,13 @@ import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry import com.karumi.jetpack.superheroes.R import com.karumi.jetpack.superheroes.SuperHeroesApplication -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.mock import org.junit.Before import org.junit.runner.RunWith import org.kodein.di.Kodein import org.kodein.di.erased.bind import org.kodein.di.erased.instance import org.mockito.MockitoAnnotations -import java.util.concurrent.ExecutorService -import java.util.concurrent.FutureTask +import java.util.concurrent.Executor @LargeTest @RunWith(AndroidJUnit4::class) @@ -26,22 +23,15 @@ abstract class FragmentTest : ScreenshotTest { abstract val fragmentBlock: () -> F - private val executorServiceOnUiThread = mock { - on(it.execute(any())).thenAnswer { invocation -> - InstrumentationRegistry.getInstrumentation().runOnMainSync { - (invocation.getArgument(0) as Runnable).run() - } - FutureTask { null } - } - } - @Before fun setup() { MockitoAnnotations.initMocks(this) val app = ApplicationProvider.getApplicationContext() app.override(Kodein.Module("Base test dependencies", allowSilentOverride = true) { import(testDependencies, allowOverride = true) - bind() with instance(executorServiceOnUiThread) + bind() with instance(Executor { + InstrumentationRegistry.getInstrumentation().runOnMainSync { it.run() } + }) }) } diff --git a/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt b/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt new file mode 100644 index 0000000..65d0b69 --- /dev/null +++ b/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt @@ -0,0 +1,84 @@ +package com.karumi.jetpack.superheroes.worker + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.testing.WorkManagerTestInitHelper +import com.karumi.jetpack.superheroes.SuperHeroesApplication +import com.karumi.jetpack.superheroes.common.SuperHeroesDatabase +import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroEntity +import com.karumi.jetpack.superheroes.domain.model.SuperHero +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.kodein.di.Kodein +import org.kodein.di.erased.bind +import org.kodein.di.erased.instance +import java.util.concurrent.Executor + +class ThanosWorkerTest { + + private lateinit var database: SuperHeroesDatabase + + @Before + fun setUp() { + val app = ApplicationProvider.getApplicationContext() + WorkManagerTestInitHelper.initializeTestWorkManager(app) + database = Room.inMemoryDatabaseBuilder(app, SuperHeroesDatabase::class.java) + .allowMainThreadQueries() + .build() + app.override(Kodein.Module("Test dependencies", allowSilentOverride = true) { + bind() with instance(database) + bind() with instance(Executor { + getInstrumentation().runOnMainSync { it.run() } + }) + }) + database.superHeroesDao().deleteAll() + } + + @Test + fun thanosWorkerDoNotDeleteAnyoneIfSuperHeroesAreLessThanTwo() { + val superHeroes = insertSuperHeroes(1) + + enqueueWorker() + + assertEquals(superHeroes, getInsertedSuperHeroes()) + } + + @Test + fun thanosWorkerDeletesHalfTheSuperHeroesWhenThereAreMoreThanTwo() { + insertSuperHeroes(10) + + enqueueWorker() + + assertEquals(5, getInsertedSuperHeroes().size) + } + + @Test + fun thanosWorkerDeletesHalfTheSuperHeroesRoundingDownWhenThereIsAnOddNumberOfSuperHeroes() { + insertSuperHeroes(11) + + enqueueWorker() + + assertEquals(6, getInsertedSuperHeroes().size) + } + + private fun enqueueWorker() = getInstrumentation().runOnMainSync { + val work = OneTimeWorkRequest.Builder(ThanosWorker::class.java).build() + WorkManager.getInstance().enqueue(work) + } + + private fun insertSuperHeroes(numberOfSuperHeroes: Int): List { + val superHeroes = (1..numberOfSuperHeroes) + .map { SuperHero("#$it", "SH $it", null, false, "") } + + database.superHeroesDao().insertAll(superHeroes.map { SuperHeroEntity(it) }) + + return superHeroes + } + + private fun getInsertedSuperHeroes(): List = + database.superHeroesDao().getAllSuperHeroes().map { it.superHero } +} \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt b/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt index a47d37a..2b52683 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt @@ -1,7 +1,8 @@ package com.karumi.jetpack.superheroes import android.app.Application -import androidx.work.OneTimeWorkRequest +import androidx.work.PeriodicWorkRequest +import androidx.work.PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS import androidx.work.WorkManager import com.karumi.jetpack.superheroes.common.SuperHeroesDatabase import com.karumi.jetpack.superheroes.common.module @@ -19,8 +20,9 @@ import org.kodein.di.erased.bind import org.kodein.di.erased.instance import org.kodein.di.erased.provider import org.kodein.di.erased.singleton -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit class SuperHeroesApplication : Application(), KodeinAware { override var kodein = Kodein.lazy { @@ -34,8 +36,11 @@ class SuperHeroesApplication : Application(), KodeinAware { } private fun startThanosWork() { - val work = OneTimeWorkRequest.Builder(ThanosWorker::class.java) - .build() + val work = PeriodicWorkRequest.Builder( + ThanosWorker::class.java, + MIN_PERIODIC_INTERVAL_MILLIS, + TimeUnit.MILLISECONDS + ).build() WorkManager.getInstance().enqueue(work) } @@ -67,7 +72,7 @@ class SuperHeroesApplication : Application(), KodeinAware { bind() with provider { RemoteSuperHeroDataSource(instance()) } - bind() with provider { + bind() with provider { Executors.newCachedThreadPool() } bind() with provider { this } diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt index 52bf4d3..acc307f 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt @@ -1,6 +1,5 @@ package com.karumi.jetpack.superheroes.data.repository -import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import androidx.paging.LivePagedListBuilder @@ -8,11 +7,11 @@ import androidx.paging.PagedList import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroDao import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroEntity import com.karumi.jetpack.superheroes.domain.model.SuperHero -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor class LocalSuperHeroDataSource( private val dao: SuperHeroDao, - private val executor: ExecutorService + private val executor: Executor ) { fun getAllSuperHeroes( pageSize: Int, @@ -36,20 +35,7 @@ class LocalSuperHeroDataSource( return superHero } - fun deleteRandomHalf() = executor.execute { - val superHeroes = dao.getAllSuperHeroes() - - if (superHeroes.size < 2) { - return@execute - } - - val randomSuperHeroIds = superHeroes - .shuffled() - .take(superHeroes.size / 2) - .map { it.superHero.id } - Log.d("GERSIO", "Deleting: $randomSuperHeroIds") - dao.deleteAll(randomSuperHeroIds) - } + fun deleteRandomHalf() = dao.deleteHalf() private fun SuperHeroEntity.toSuperHero(): SuperHero = superHero private fun SuperHero.toEntity(): SuperHeroEntity = SuperHeroEntity(this) diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt index 196a4d6..32a3d98 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt @@ -3,10 +3,10 @@ package com.karumi.jetpack.superheroes.data.repository import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.karumi.jetpack.superheroes.domain.model.SuperHero -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor class RemoteSuperHeroDataSource( - private val executor: ExecutorService + private val executor: Executor ) { companion object { private const val BIT_TIME = 1500L diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt index b1653e8..0601628 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt @@ -6,6 +6,7 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import androidx.room.Update @Dao @@ -30,4 +31,20 @@ interface SuperHeroDao { @Query("DELETE FROM superheroes WHERE superhero_id IN (:ids)") fun deleteAll(ids: List) + + @Transaction + fun deleteHalf() { + val superHeroes = getAllSuperHeroes() + + if (superHeroes.size < 2) { + return + } + + val randomSuperHeroIds = superHeroes + .shuffled() + .take(superHeroes.size / 2) + .map { it.superHero.id } + + deleteAll(randomSuperHeroIds) + } } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt b/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt index 2d6a7a2..0103546 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt @@ -1,7 +1,6 @@ package com.karumi.jetpack.superheroes.worker import android.content.Context -import android.util.Log import androidx.work.Worker import androidx.work.WorkerParameters import com.karumi.jetpack.superheroes.data.repository.LocalSuperHeroDataSource @@ -18,7 +17,6 @@ class ThanosWorker( private val localSuperHeroesDataSource: LocalSuperHeroDataSource by instance() override fun doWork(): Result { - Log.d("GERSIO", "doWork!") localSuperHeroesDataSource.deleteRandomHalf() return Result.success() }