From fb4a004df005f47ecb2a2048ac8547746a366da8 Mon Sep 17 00:00:00 2001 From: Mateusz Armatys Date: Wed, 19 Oct 2022 12:07:55 +0200 Subject: [PATCH] test(coreexample): Add `MockTestRule` which adds common configuration for tests with server mocks and hilt-testing. --- .../core/test/android/uitests/BaseMockTest.kt | 44 ++++++++++++ .../core/test/android/uitests/MockTestRule.kt | 72 +++++++++++++++++++ .../android/uitests/giap/GiapUpgradeTests.kt | 41 ++--------- .../uitests/giap/SignupWithGoogleIapTests.kt | 56 ++++++--------- .../api/test-android-instrumented.api | 2 + .../android/robots/payments/GoogleIAPRobot.kt | 8 +++ 6 files changed, 154 insertions(+), 69 deletions(-) create mode 100644 coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/BaseMockTest.kt create mode 100644 coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/MockTestRule.kt diff --git a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/BaseMockTest.kt b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/BaseMockTest.kt new file mode 100644 index 000000000..4bd2e42d5 --- /dev/null +++ b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/BaseMockTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 Proton Technologies AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + +package me.proton.core.test.android.uitests + +import dagger.hilt.android.testing.BindValue +import dagger.hilt.android.testing.HiltAndroidTest +import me.proton.core.network.data.di.BaseProtonApiUrl +import okhttp3.HttpUrl +import org.junit.Rule + +interface BaseMockTest { + @BaseProtonApiUrl + var baseProtonApiUrl: HttpUrl + + val testUsername get() = "test-mock-936" + val testPassword get() = "password" +} + +/** A minimal example for writing a test with hilt-testing and mocked server. */ +@HiltAndroidTest +class SampleMockTest : BaseMockTest { + @get:Rule + val mockTestRule = MockTestRule(this) + + @BindValue + @BaseProtonApiUrl + override lateinit var baseProtonApiUrl: HttpUrl +} diff --git a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/MockTestRule.kt b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/MockTestRule.kt new file mode 100644 index 000000000..04ff1b9ec --- /dev/null +++ b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/MockTestRule.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2022 Proton Technologies AG + * This file is part of Proton AG and ProtonCore. + * + * ProtonCore is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ProtonCore is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with ProtonCore. If not, see . + */ + +package me.proton.core.test.android.uitests + +import dagger.hilt.android.testing.HiltAndroidRule +import me.proton.android.core.coreexample.CoreExampleLogger +import me.proton.core.test.android.TestWebServerDispatcher +import me.proton.core.test.android.instrumented.utils.Shell +import me.proton.core.util.kotlin.CoreLogger +import okhttp3.mockwebserver.MockWebServer +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import timber.log.Timber + +/** A test rule that applies [HiltAndroidRule] and prepares a mock [MockWebServer]. + * @see SampleMockTest + */ +class MockTestRule constructor(private val testInstance: BaseMockTest) : TestRule { + lateinit var dispatcher: TestWebServerDispatcher + + override fun apply(base: Statement, description: Description): Statement { + initLogging() + + dispatcher = TestWebServerDispatcher() + val webServer = MockWebServer() + webServer.dispatcher = dispatcher + + val hiltRule = HiltAndroidRule(testInstance) + val result = hiltRule.apply(object : Statement() { + override fun evaluate() { + testInstance.baseProtonApiUrl = webServer.url("/") + hiltRule.inject() + base.evaluate() + } + }, description) + + return object : Statement() { + override fun evaluate() { + try { + result.evaluate() + } catch (t: Throwable) { + Shell.saveToFile(description) + throw t + } finally { + webServer.shutdown() + } + } + } + } + + private fun initLogging() { + Timber.plant(Timber.DebugTree()) + CoreLogger.set(CoreExampleLogger()) + } +} diff --git a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/GiapUpgradeTests.kt b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/GiapUpgradeTests.kt index 6a10a2cd0..181a1c068 100644 --- a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/GiapUpgradeTests.kt +++ b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/GiapUpgradeTests.kt @@ -21,9 +21,7 @@ package me.proton.core.test.android.uitests.giap import androidx.test.core.app.ActivityScenario import com.android.billingclient.api.BillingClient import dagger.hilt.android.testing.BindValue -import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import me.proton.android.core.coreexample.CoreExampleLogger import me.proton.android.core.coreexample.MainActivity import me.proton.core.network.data.di.BaseProtonApiUrl import me.proton.core.test.android.TestWebServerDispatcher @@ -31,55 +29,30 @@ import me.proton.core.test.android.mocks.FakeBillingClientFactory import me.proton.core.test.android.mocks.mockBillingClientSuccess import me.proton.core.test.android.robots.auth.AddAccountRobot import me.proton.core.test.android.robots.payments.GoogleIAPRobot +import me.proton.core.test.android.uitests.BaseMockTest import me.proton.core.test.android.uitests.CoreexampleRobot -import me.proton.core.util.kotlin.CoreLogger +import me.proton.core.test.android.uitests.MockTestRule import okhttp3.HttpUrl -import okhttp3.mockwebserver.MockWebServer import org.junit.Rule -import timber.log.Timber import javax.inject.Inject -import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test import me.proton.core.test.android.plugins.data.Plan as TestPlan @HiltAndroidTest -class GiapUpgradeTests { - private val testUsername = "test-mock-936" - private val testPassword = "password" - - @get:Rule(order = Rule.DEFAULT_ORDER - 1) - val hiltAndroidRule = HiltAndroidRule(this) +class GiapUpgradeTests: BaseMockTest { + @get:Rule + val mockTestRule = MockTestRule(this) @BindValue @BaseProtonApiUrl - lateinit var baseProtonApiUrl: HttpUrl + override lateinit var baseProtonApiUrl: HttpUrl @Inject lateinit var billingClientFactory: FakeBillingClientFactory private val billingClient: BillingClient get() = billingClientFactory.billingClient - private lateinit var dispatcher: TestWebServerDispatcher - private lateinit var webServer: MockWebServer - @BeforeTest - fun setUp() { - Timber.plant(Timber.DebugTree()) - CoreLogger.set(CoreExampleLogger()) - - dispatcher = TestWebServerDispatcher() - webServer = MockWebServer().apply { - dispatcher = this@GiapUpgradeTests.dispatcher - } - baseProtonApiUrl = webServer.url("/") - - hiltAndroidRule.inject() - } - - @AfterTest - fun tearDown() { - webServer.shutdown() - } + private val dispatcher: TestWebServerDispatcher get() = mockTestRule.dispatcher @Test fun freeUserUpgradeAllProvidersAvailable() { diff --git a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/SignupWithGoogleIapTests.kt b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/SignupWithGoogleIapTests.kt index c91468d8c..02fa75e49 100644 --- a/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/SignupWithGoogleIapTests.kt +++ b/coreexample/src/androidTestMock/kotlin/me/proton/core/test/android/uitests/giap/SignupWithGoogleIapTests.kt @@ -24,9 +24,7 @@ import androidx.test.core.app.ApplicationProvider import com.android.billingclient.api.BillingClient import com.android.billingclient.api.BillingClient.BillingResponseCode import dagger.hilt.android.testing.BindValue -import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest -import me.proton.android.core.coreexample.CoreExampleLogger import me.proton.core.auth.presentation.ui.AddAccountActivity import me.proton.core.network.data.di.BaseProtonApiUrl import me.proton.core.plan.presentation.entity.PlanInput @@ -41,56 +39,30 @@ import me.proton.core.test.android.robots.auth.signup.RecoveryMethodsRobot import me.proton.core.test.android.robots.auth.signup.SignupFinishedRobot import me.proton.core.test.android.robots.payments.GoogleIAPRobot import me.proton.core.test.android.robots.plans.SelectPlanRobot -import me.proton.core.util.kotlin.CoreLogger +import me.proton.core.test.android.uitests.BaseMockTest +import me.proton.core.test.android.uitests.MockTestRule import okhttp3.HttpUrl -import okhttp3.mockwebserver.MockWebServer import org.junit.Rule -import timber.log.Timber import javax.inject.Inject -import kotlin.test.AfterTest -import kotlin.test.BeforeTest import kotlin.test.Test import me.proton.core.paymentiap.presentation.R as PaymentIapR import me.proton.core.test.android.plugins.data.Plan as TestPlan @HiltAndroidTest -class SignupWithGoogleIapTests { - private val testUsername = "test-mock-936" - private val testPassword = "password" - - @get:Rule(order = Rule.DEFAULT_ORDER - 1) - val hiltAndroidRule = HiltAndroidRule(this) +class SignupWithGoogleIapTests : BaseMockTest { + @get:Rule + val mockTestRule = MockTestRule(this) @BindValue @BaseProtonApiUrl - lateinit var baseProtonApiUrl: HttpUrl + override lateinit var baseProtonApiUrl: HttpUrl @Inject lateinit var billingClientFactory: FakeBillingClientFactory private val appContext: Context get() = ApplicationProvider.getApplicationContext() private val billingClient: BillingClient get() = billingClientFactory.billingClient - private lateinit var dispatcher: TestWebServerDispatcher - private lateinit var webServer: MockWebServer - - @BeforeTest - fun setUp() { - Timber.plant(Timber.DebugTree()) - CoreLogger.set(CoreExampleLogger()) - - dispatcher = TestWebServerDispatcher() - webServer = MockWebServer().apply { - dispatcher = this@SignupWithGoogleIapTests.dispatcher - } - baseProtonApiUrl = webServer.url("/") - - hiltAndroidRule.inject() - } - - @AfterTest - fun tearDown() { - webServer.shutdown() - } + private val dispatcher: TestWebServerDispatcher get() = mockTestRule.dispatcher @Test fun googleBillingNotAvailable() { @@ -137,6 +109,13 @@ class SignupWithGoogleIapTests { .skipConfirm() .toggleExpandPlan(TestPlan.Plus) .selectPlan(TestPlan.Plus) + .apply { + verify { + payWithGoogleButtonIsClickable() + payWithCardButtonIsNotVisible() + switchPaymentProviderButtonIsNotVisible() + } + } .payWithGPay() .verify { signupFinishedDisplayed() @@ -169,6 +148,13 @@ class SignupWithGoogleIapTests { .skipConfirm() .toggleExpandPlan(TestPlan.Plus) .selectPlan(TestPlan.Plus) + .apply { + verify { + payWithGoogleButtonIsClickable() + payWithCardButtonIsNotVisible() + switchPaymentProviderButtonIsVisible() + } + } .payWithGPay() .verify { signupFinishedDisplayed() diff --git a/test/android/instrumented/api/test-android-instrumented.api b/test/android/instrumented/api/test-android-instrumented.api index a0c479bbb..f6504e58b 100644 --- a/test/android/instrumented/api/test-android-instrumented.api +++ b/test/android/instrumented/api/test-android-instrumented.api @@ -1063,6 +1063,8 @@ public final class me/proton/core/test/android/robots/payments/GoogleIAPRobot$Ve public fun ()V public final fun payWithCardButtonIsNotVisible ()V public final fun payWithGoogleButtonIsClickable ()V + public final fun switchPaymentProviderButtonIsNotVisible ()V + public final fun switchPaymentProviderButtonIsVisible ()V } public class me/proton/core/test/android/robots/payments/PaymentRobot : me/proton/core/test/android/robots/CoreRobot { diff --git a/test/android/instrumented/src/main/kotlin/me/proton/core/test/android/robots/payments/GoogleIAPRobot.kt b/test/android/instrumented/src/main/kotlin/me/proton/core/test/android/robots/payments/GoogleIAPRobot.kt index 3b70ed32c..c7ab8a954 100644 --- a/test/android/instrumented/src/main/kotlin/me/proton/core/test/android/robots/payments/GoogleIAPRobot.kt +++ b/test/android/instrumented/src/main/kotlin/me/proton/core/test/android/robots/payments/GoogleIAPRobot.kt @@ -37,6 +37,14 @@ class GoogleIAPRobot : PaymentRobot() { view.withId(R.id.payButton).checkNotDisplayed() } + fun switchPaymentProviderButtonIsNotVisible() { + view.withId(R.id.nextPaymentProviderButton).checkNotDisplayed() + } + + fun switchPaymentProviderButtonIsVisible() { + view.withId(R.id.nextPaymentProviderButton).checkDisplayed() + } + fun payWithGoogleButtonIsClickable() { view.withId(R.id.gPayButton) .checkDisplayed()