diff options
| author | David Göransson <david.goransson@mullvad.net> | 2025-08-05 10:15:27 +0200 |
|---|---|---|
| committer | David Göransson <david.goransson@mullvad.net> | 2025-08-05 10:15:27 +0200 |
| commit | f240b49d638f3f9932de70dbb00d108e33b7670f (patch) | |
| tree | 75a71833287de24104ee99b4e19b5a90f74bb3e2 /android | |
| parent | beed5dc2c6e161d431bd455ff42e262899224bd4 (diff) | |
| parent | 25d23887cc7f4fdd2e11cb39366489d49f33eab7 (diff) | |
| download | mullvadvpn-f240b49d638f3f9932de70dbb00d108e33b7670f.tar.xz mullvadvpn-f240b49d638f3f9932de70dbb00d108e33b7670f.zip | |
Merge branch 'flaky-test-addtimeviewmodeltest-droid-2101'
Diffstat (limited to 'android')
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" + } + } + } } |
