Skip to content

Commit 47afddb

Browse files
authored
Simplify running suspending tests (#1452)
* Move LoggerModule to di package and add TestLoggerModule * Remove main dispatcher injection and use runTest directly * Add verbose logging module for tests * Add test for UnifiedPushService.onUnregistered
1 parent 5f647b7 commit 47afddb

File tree

8 files changed

+60
-40
lines changed

8 files changed

+60
-40
lines changed

app/src/androidTest/kotlin/at/bitfire/davdroid/HiltTestRunner.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,20 @@ import android.content.Context
99
import android.os.Build
1010
import android.os.Bundle
1111
import androidx.test.runner.AndroidJUnitRunner
12+
import at.bitfire.davdroid.di.TestCoroutineDispatchersModule
1213
import at.bitfire.davdroid.sync.SyncAdapterService
1314
import dagger.hilt.android.testing.HiltTestApplication
15+
import kotlinx.coroutines.Dispatchers
16+
import kotlinx.coroutines.ExperimentalCoroutinesApi
17+
import kotlinx.coroutines.test.setMain
1418

1519
@Suppress("unused")
1620
class HiltTestRunner : AndroidJUnitRunner() {
1721

1822
override fun newApplication(cl: ClassLoader, name: String, context: Context): Application =
1923
super.newApplication(cl, HiltTestApplication::class.java.name, context)
2024

25+
@OptIn(ExperimentalCoroutinesApi::class)
2126
override fun onCreate(arguments: Bundle?) {
2227
super.onCreate(arguments)
2328

@@ -27,6 +32,9 @@ class HiltTestRunner : AndroidJUnitRunner() {
2732

2833
// disable sync adapters
2934
SyncAdapterService.syncActive.set(false)
35+
36+
// set main dispatcher for tests (especially runTest)
37+
Dispatchers.setMain(TestCoroutineDispatchersModule.standardTestDispatcher)
3038
}
3139

3240
}

app/src/androidTest/kotlin/at/bitfire/davdroid/di/TestCoroutineDispatchersModule.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
package at.bitfire.davdroid.di
66

7+
import at.bitfire.davdroid.di.TestCoroutineDispatchersModule.standardTestDispatcher
78
import dagger.Module
89
import dagger.Provides
910
import dagger.hilt.components.SingletonComponent
@@ -13,8 +14,10 @@ import kotlinx.coroutines.test.StandardTestDispatcher
1314
import kotlinx.coroutines.test.TestCoroutineScheduler
1415

1516
/**
16-
* If you run tests that switch context to one of these dispatchers, use `runTest(mainDispatcher)`
17-
* with `mainDispatcher` being an injected [MainDispatcher] instead of plain `runTest`.
17+
* Provides test dispatchers to be injected instead of the normal ones.
18+
*
19+
* The [standardTestDispatcher] is set as main dispatcher in [at.bitfire.davdroid.HiltTestRunner],
20+
* so that tests can just use [kotlinx.coroutines.test.runTest] without providing [standardTestDispatcher].
1821
*/
1922
@Module
2023
@TestInstallIn(
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
3+
*/
4+
5+
package at.bitfire.davdroid.di
6+
7+
import at.bitfire.davdroid.log.LogcatHandler
8+
import dagger.Module
9+
import dagger.Provides
10+
import dagger.hilt.components.SingletonComponent
11+
import dagger.hilt.testing.TestInstallIn
12+
import java.util.logging.Level
13+
import java.util.logging.Logger
14+
import javax.inject.Singleton
15+
16+
/**
17+
* Module that provides verbose logging for tests.
18+
*/
19+
@TestInstallIn(
20+
components = [SingletonComponent::class],
21+
replaces = [LoggerModule::class]
22+
)
23+
@Module
24+
class TestLoggerModule {
25+
26+
@Provides
27+
@Singleton
28+
fun logger(): Logger = Logger.getGlobal().apply {
29+
level = Level.ALL
30+
addHandler(LogcatHandler())
31+
}
32+
33+
}

app/src/androidTest/kotlin/at/bitfire/davdroid/push/UnifiedPushServiceTest.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import android.content.Context
88
import android.content.Intent
99
import android.os.IBinder
1010
import androidx.test.rule.ServiceTestRule
11-
import at.bitfire.davdroid.di.MainDispatcher
1211
import dagger.hilt.android.qualifiers.ApplicationContext
1312
import dagger.hilt.android.testing.BindValue
1413
import dagger.hilt.android.testing.HiltAndroidRule
@@ -19,7 +18,6 @@ import io.mockk.every
1918
import io.mockk.impl.annotations.RelaxedMockK
2019
import io.mockk.junit4.MockKRule
2120
import io.mockk.mockk
22-
import kotlinx.coroutines.CoroutineDispatcher
2321
import kotlinx.coroutines.ExperimentalCoroutinesApi
2422
import kotlinx.coroutines.test.advanceUntilIdle
2523
import kotlinx.coroutines.test.runTest
@@ -48,10 +46,6 @@ class UnifiedPushServiceTest {
4846
@ApplicationContext
4947
lateinit var context: Context
5048

51-
@Inject
52-
@MainDispatcher
53-
lateinit var mainDispatcher: CoroutineDispatcher
54-
5549
@RelaxedMockK
5650
@BindValue
5751
lateinit var pushRegistrationManager: PushRegistrationManager
@@ -70,7 +64,7 @@ class UnifiedPushServiceTest {
7064

7165

7266
@Test
73-
fun testOnNewEndpoint() = runTest(mainDispatcher) {
67+
fun testOnNewEndpoint() = runTest {
7468
val endpoint = mockk<PushEndpoint> {
7569
every { url } returns "https://example.com/12"
7670
}
@@ -84,7 +78,7 @@ class UnifiedPushServiceTest {
8478
}
8579

8680
@Test
87-
fun testOnRegistrationFailed() = runTest(mainDispatcher) {
81+
fun testOnRegistrationFailed() = runTest {
8882
unifiedPushService.onRegistrationFailed(FailedReason.INTERNAL_ERROR, "34")
8983

9084
advanceUntilIdle()
@@ -95,8 +89,8 @@ class UnifiedPushServiceTest {
9589
}
9690

9791
@Test
98-
fun testOnUnregistered() = runTest(mainDispatcher) {
99-
unifiedPushService.onRegistrationFailed(FailedReason.INTERNAL_ERROR, "45")
92+
fun testOnUnregistered() = runTest {
93+
unifiedPushService.onUnregistered("45")
10094

10195
advanceUntilIdle()
10296
coVerify {

app/src/androidTest/kotlin/at/bitfire/davdroid/ui/CollectionSelectedUseCaseTest.kt

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ package at.bitfire.davdroid.ui
66

77
import at.bitfire.davdroid.db.Collection
88
import at.bitfire.davdroid.db.Service
9-
import at.bitfire.davdroid.di.MainDispatcher
109
import at.bitfire.davdroid.push.PushRegistrationManager
1110
import at.bitfire.davdroid.repository.DavCollectionRepository
1211
import at.bitfire.davdroid.repository.DavServiceRepository
@@ -17,7 +16,6 @@ import dagger.hilt.android.testing.HiltAndroidTest
1716
import io.mockk.coVerify
1817
import io.mockk.impl.annotations.RelaxedMockK
1918
import io.mockk.junit4.MockKRule
20-
import kotlinx.coroutines.CoroutineDispatcher
2119
import kotlinx.coroutines.ExperimentalCoroutinesApi
2220
import kotlinx.coroutines.test.advanceUntilIdle
2321
import kotlinx.coroutines.test.runTest
@@ -48,10 +46,6 @@ class CollectionSelectedUseCaseTest {
4846
@Inject
4947
lateinit var collectionRepository: DavCollectionRepository
5048

51-
@Inject
52-
@MainDispatcher
53-
lateinit var mainDispatcher: CoroutineDispatcher
54-
5549
val service = Service(
5650
id = 1,
5751
type = Service.Companion.TYPE_CALDAV,
@@ -87,7 +81,7 @@ class CollectionSelectedUseCaseTest {
8781

8882

8983
@Test
90-
fun testHandleWithDelay() = runTest(mainDispatcher) {
84+
fun testHandleWithDelay() = runTest {
9185
useCase.handleWithDelay(collectionId = collection.id)
9286

9387
advanceUntilIdle()

app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/DavDocumentsProviderTest.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import android.security.NetworkSecurityPolicy
99
import at.bitfire.davdroid.db.AppDatabase
1010
import at.bitfire.davdroid.db.WebDavDocument
1111
import at.bitfire.davdroid.db.WebDavMount
12-
import at.bitfire.davdroid.di.MainDispatcher
1312
import at.bitfire.davdroid.network.HttpClient
1413
import dagger.hilt.android.qualifiers.ApplicationContext
1514
import dagger.hilt.android.testing.HiltAndroidRule
1615
import dagger.hilt.android.testing.HiltAndroidTest
1716
import io.mockk.junit4.MockKRule
18-
import kotlinx.coroutines.CoroutineDispatcher
1917
import kotlinx.coroutines.test.runTest
2018
import okhttp3.CookieJar
2119
import okhttp3.mockwebserver.Dispatcher
@@ -52,10 +50,6 @@ class DavDocumentsProviderTest {
5250

5351
@Inject
5452
lateinit var db: AppDatabase
55-
56-
@Inject
57-
@MainDispatcher
58-
lateinit var mainDispatcher: CoroutineDispatcher
5953

6054
@Inject
6155
lateinit var testDispatcher: TestDispatcher
@@ -92,7 +86,7 @@ class DavDocumentsProviderTest {
9286

9387

9488
@Test
95-
fun testDoQueryChildren_insert() = runTest(mainDispatcher) {
89+
fun testDoQueryChildren_insert() = runTest {
9690
// Create parent and root in database
9791
val id = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", server.url(PATH_WEBDAV_ROOT)))
9892
val webDavMount = db.webDavMountDao().getById(id)
@@ -113,7 +107,7 @@ class DavDocumentsProviderTest {
113107
}
114108

115109
@Test
116-
fun testDoQueryChildren_update() = runTest(mainDispatcher) {
110+
fun testDoQueryChildren_update() = runTest {
117111
// Create parent and root in database
118112
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", server.url(PATH_WEBDAV_ROOT)))
119113
val webDavMount = db.webDavMountDao().getById(mountId)
@@ -149,7 +143,7 @@ class DavDocumentsProviderTest {
149143
}
150144

151145
@Test
152-
fun testDoQueryChildren_delete() = runTest(mainDispatcher) {
146+
fun testDoQueryChildren_delete() = runTest {
153147
// Create parent and root in database
154148
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", server.url(PATH_WEBDAV_ROOT)))
155149
val webDavMount = db.webDavMountDao().getById(mountId)
@@ -173,7 +167,7 @@ class DavDocumentsProviderTest {
173167
}
174168

175169
@Test
176-
fun testDoQueryChildren_updateTwoDirectoriesSimultaneously() = runTest(mainDispatcher) {
170+
fun testDoQueryChildren_updateTwoDirectoriesSimultaneously() = runTest {
177171
// Create root in database
178172
val mountId = db.webDavMountDao().insert(WebDavMount(0, "Cat food storage", server.url(PATH_WEBDAV_ROOT)))
179173
val webDavMount = db.webDavMountDao().getById(mountId)

app/src/androidTest/kotlin/at/bitfire/davdroid/webdav/WebDavMountRepositoryTest.kt

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@
44

55
package at.bitfire.davdroid.webdav
66

7-
import at.bitfire.davdroid.di.MainDispatcher
87
import dagger.hilt.android.testing.HiltAndroidRule
98
import dagger.hilt.android.testing.HiltAndroidTest
10-
import kotlinx.coroutines.CoroutineDispatcher
119
import kotlinx.coroutines.test.runTest
1210
import okhttp3.mockwebserver.MockResponse
1311
import okhttp3.mockwebserver.MockWebServer
@@ -24,10 +22,6 @@ class WebDavMountRepositoryTest {
2422
@get:Rule
2523
val hiltRule = HiltAndroidRule(this)
2624

27-
@Inject
28-
@MainDispatcher
29-
lateinit var mainDispatcher: CoroutineDispatcher
30-
3125
@Inject
3226
lateinit var repository: WebDavMountRepository
3327

@@ -40,29 +34,29 @@ class WebDavMountRepositoryTest {
4034
val url = web.url("/")
4135

4236
@Test
43-
fun testHasWebDav_NoDavHeader() = runTest(mainDispatcher) {
37+
fun testHasWebDav_NoDavHeader() = runTest {
4438
web.enqueue(MockResponse().setResponseCode(200))
4539
assertFalse(repository.hasWebDav(url, null))
4640
}
4741

4842
@Test
49-
fun testHasWebDav_DavClass1() = runTest(mainDispatcher) {
43+
fun testHasWebDav_DavClass1() = runTest {
5044
web.enqueue(MockResponse()
5145
.setResponseCode(200)
5246
.addHeader("DAV: 1"))
5347
assertTrue(repository.hasWebDav(url, null))
5448
}
5549

5650
@Test
57-
fun testHasWebDav_DavClass2() = runTest(mainDispatcher) {
51+
fun testHasWebDav_DavClass2() = runTest {
5852
web.enqueue(MockResponse()
5953
.setResponseCode(200)
6054
.addHeader("DAV: 1, 2"))
6155
assertTrue(repository.hasWebDav(url, null))
6256
}
6357

6458
@Test
65-
fun testHasWebDav_DavClass3() = runTest(mainDispatcher) {
59+
fun testHasWebDav_DavClass3() = runTest {
6660
web.enqueue(MockResponse()
6761
.setResponseCode(200)
6862
.addHeader("DAV: 1, 3"))

app/src/main/kotlin/at/bitfire/davdroid/log/LoggerModule.kt renamed to app/src/main/kotlin/at/bitfire/davdroid/di/LoggerModule.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
33
*/
44

5-
package at.bitfire.davdroid.log
5+
package at.bitfire.davdroid.di
66

77
import dagger.Module
88
import dagger.Provides

0 commit comments

Comments
 (0)