summaryrefslogtreecommitdiffhomepage
path: root/android
diff options
context:
space:
mode:
authorDavid Göransson <david.goransson@mullvad.net>2025-08-05 10:15:27 +0200
committerDavid Göransson <david.goransson@mullvad.net>2025-08-05 10:15:27 +0200
commitf240b49d638f3f9932de70dbb00d108e33b7670f (patch)
tree75a71833287de24104ee99b4e19b5a90f74bb3e2 /android
parentbeed5dc2c6e161d431bd455ff42e262899224bd4 (diff)
parent25d23887cc7f4fdd2e11cb39366489d49f33eab7 (diff)
downloadmullvadvpn-f240b49d638f3f9932de70dbb00d108e33b7670f.tar.xz
mullvadvpn-f240b49d638f3f9932de70dbb00d108e33b7670f.zip
Merge branch 'flaky-test-addtimeviewmodeltest-droid-2101'
Diffstat (limited to 'android')
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt9
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AddTimeViewModelTest.kt3
-rw-r--r--android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt3
-rw-r--r--android/lib/common-test/src/main/java/net/mullvad/mullvadvpn/lib/common/test/TestCoroutineRule.kt6
-rw-r--r--android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTest.kt18
5 files changed, 30 insertions, 9 deletions
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
index 0fd1563169..e494aa41b7 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/usecase/OutOfTimeUseCaseTest.kt
@@ -9,7 +9,6 @@ import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -17,10 +16,9 @@ import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
import net.mullvad.mullvadvpn.data.mock
+import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.model.AccountData
import net.mullvad.mullvadvpn.lib.model.AuthFailedError
import net.mullvad.mullvadvpn.lib.model.ErrorState
@@ -31,8 +29,10 @@ import net.mullvad.mullvadvpn.lib.shared.ConnectionProxy
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
@ExperimentalCoroutinesApi
+@ExtendWith(TestCoroutineRule::class)
class OutOfTimeUseCaseTest {
private val mockAccountRepository: AccountRepository = mockk()
private val mockConnectionProxy: ConnectionProxy = mockk()
@@ -52,15 +52,12 @@ class OutOfTimeUseCaseTest {
every { mockAccountRepository.accountData } returns expiry
every { mockConnectionProxy.tunnelState } returns events.consumeAsFlow()
- Dispatchers.setMain(dispatcher)
-
outOfTimeUseCase =
OutOfTimeUseCase(mockConnectionProxy, mockAccountRepository, scope.backgroundScope)
}
@AfterEach
fun teardown() {
- Dispatchers.resetMain()
unmockkAll()
}
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AddTimeViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AddTimeViewModelTest.kt
index f9497bd159..b042f44b9c 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AddTimeViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/AddTimeViewModelTest.kt
@@ -16,6 +16,7 @@ import kotlinx.coroutines.test.runTest
import net.mullvad.mullvadvpn.compose.state.AddTimeUiState
import net.mullvad.mullvadvpn.compose.state.PaymentState
import net.mullvad.mullvadvpn.compose.state.PurchaseState
+import net.mullvad.mullvadvpn.lib.common.test.TestCoroutineRule
import net.mullvad.mullvadvpn.lib.common.test.assertLists
import net.mullvad.mullvadvpn.lib.model.TunnelState
import net.mullvad.mullvadvpn.lib.payment.model.PaymentAvailability
@@ -30,7 +31,9 @@ import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.util.Lc
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.extension.ExtendWith
+@ExtendWith(TestCoroutineRule::class)
class AddTimeViewModelTest {
private val mockPaymentUseCase: PaymentUseCase = mockk()
diff --git a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
index 09a9ff8bdb..2c246d8eff 100644
--- a/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
+++ b/android/app/src/test/kotlin/net/mullvad/mullvadvpn/viewmodel/LoginViewModelTest.kt
@@ -13,12 +13,10 @@ import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import java.time.ZonedDateTime
import kotlin.test.assertIs
-import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.test.setMain
import net.mullvad.mullvadvpn.compose.state.LoginError
import net.mullvad.mullvadvpn.compose.state.LoginState.Idle
import net.mullvad.mullvadvpn.compose.state.LoginState.Loading
@@ -48,7 +46,6 @@ class LoginViewModelTest {
@BeforeEach
fun setup() {
- Dispatchers.setMain(UnconfinedTestDispatcher())
MockKAnnotations.init(this, relaxUnitFun = true)
accountHistoryFlow = MutableStateFlow(null)
every { connectivityUseCase() } returns true
diff --git a/android/lib/common-test/src/main/java/net/mullvad/mullvadvpn/lib/common/test/TestCoroutineRule.kt b/android/lib/common-test/src/main/java/net/mullvad/mullvadvpn/lib/common/test/TestCoroutineRule.kt
index 34d6739119..c73516f72d 100644
--- a/android/lib/common-test/src/main/java/net/mullvad/mullvadvpn/lib/common/test/TestCoroutineRule.kt
+++ b/android/lib/common-test/src/main/java/net/mullvad/mullvadvpn/lib/common/test/TestCoroutineRule.kt
@@ -10,6 +10,12 @@ import org.junit.jupiter.api.extension.AfterEachCallback
import org.junit.jupiter.api.extension.BeforeEachCallback
import org.junit.jupiter.api.extension.ExtensionContext
+/**
+ * Should be applied to any test class that has a test subject that uses the main dispatcher. This
+ * is the default dispatcher for coroutines on Android and is for example used by the
+ * viewModelScope. This avoids test flakiness due to the test dispatcher eagerly starting coroutines
+ * instead of waiting for runCurrent for them to start.
+ */
@OptIn(ExperimentalCoroutinesApi::class)
class TestCoroutineRule(val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()) :
BeforeEachCallback, AfterEachCallback {
diff --git a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTest.kt b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTest.kt
index c7e3cc54ed..4e8de563fd 100644
--- a/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTest.kt
+++ b/android/test/arch/src/test/kotlin/net/mullvad/mullvadvpn/test/arch/classes/ClassTest.kt
@@ -27,4 +27,22 @@ class ClassTest {
it.packagee?.name?.startsWith("net.mullvad.mullvadvpn") ?: false
}
.assertTrue { it.hasNameEndingWith("Test") }
+
+ @Test
+ fun `ensure that all view model test classes are annotated with TestCoroutineRule`() =
+ Konsist.scopeFromTest()
+ .classes(includeNested = false)
+ .filter {
+ // Only include classes that are view model tests
+ // We want to ignore the class "ViewModelTest" which contains konsist tests
+ it.name.contains(".+ViewModelTest".toRegex())
+ }
+ .assertTrue {
+ it.hasAnnotation { annotation ->
+ annotation.name == "ExtendWith" &&
+ annotation.arguments.any { argument ->
+ argument.value == "TestCoroutineRule::class"
+ }
+ }
+ }
}