Updated on 2026-08-14

This commit is contained in:
Tangem 2025-07-09 11:08:16 +03:00
parent 2a835ce3c9
commit 0c56df0075
16 changed files with 306 additions and 58 deletions

View file

@ -1,7 +1,8 @@
package com.tangem.common
import android.Manifest
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.junit4.createEmptyComposeRule
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.intent.Intents
import androidx.test.rule.GrantPermissionRule
import com.kaspersky.components.alluresupport.interceptors.step.ScreenshotStepInterceptor
@ -10,8 +11,8 @@ import com.kaspersky.components.composesupport.config.addComposeSupport
import com.kaspersky.kaspresso.kaspresso.Kaspresso
import com.kaspersky.kaspresso.testcases.api.testcase.TestCase
import com.tangem.common.allure.FailedStepScreenshotInterceptor
import com.tangem.datasource.local.preferences.AppPreferencesStore
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.common.rules.ApiEnvironmentRule
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.tap.MainActivity
import dagger.hilt.android.testing.HiltAndroidRule
import org.junit.Rule
@ -35,17 +36,20 @@ abstract class BaseTestCase : TestCase(
) {
@Inject
lateinit var tangemSdkManager: TangemSdkManager
@Inject
lateinit var appPreferencesStore: AppPreferencesStore
lateinit var apiConfigsManager: ApiConfigsManager
private val hiltRule = HiltAndroidRule(this)
private val permissionRule = GrantPermissionRule.grant(
private val apiEnvironmentRule = ApiEnvironmentRule()
private val permissionRule = GrantPermissionRule.grant(
Manifest.permission.POST_NOTIFICATIONS,
Manifest.permission.CAMERA,
)
val composeTestRule = createAndroidComposeRule<MainActivity>()
/**
* It is important to use `ComposeRule` without specifying an activity to ensure that the initialization order of
* all test rules is fully controlled.
*/
val composeTestRule = createEmptyComposeRule()
@Rule
@JvmField
@ -53,18 +57,26 @@ abstract class BaseTestCase : TestCase(
.outerRule(hiltRule)
.around(ApplicationInjectionExecutionRule())
.around(permissionRule)
.around(apiEnvironmentRule)
.around(composeTestRule)
/**
* Initialization order is important:
* DI dependencies must be injected first,
* then the API environment should be set up,
* and only after that the activity should be launched.
*/
protected fun setupHooks(
additionalBeforeSection: () -> Unit = {},
additionalAfterSection: () -> Unit = {},
) = before {
hiltRule.inject()
apiEnvironmentRule.setup(apiConfigsManager)
ActivityScenario.launch(MainActivity::class.java)
Intents.init()
additionalBeforeSection()
}.after {
additionalAfterSection()
Intents.release()
}
}

View file

@ -0,0 +1,14 @@
package com.tangem.common.annotations
import com.tangem.datasource.api.common.config.ApiEnvironment
/**
* Annotation to specify the API environment for a class or function.
*
* @property environment the API environment to be used (defaults to [ApiEnvironment.MOCK])
*/
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class ApiEnv(
val environment: ApiEnvironment = ApiEnvironment.MOCK,
)

View file

@ -0,0 +1,78 @@
package com.tangem.common.rules
import androidx.test.platform.app.InstrumentationRegistry
import com.tangem.common.annotations.ApiEnv
import com.tangem.datasource.api.common.config.ApiEnvironment
import com.tangem.datasource.api.common.config.managers.ApiConfigsManager
import com.tangem.datasource.api.common.config.managers.MutableApiConfigsManager
import com.tangem.wallet.test.BuildConfig
import kotlinx.coroutines.runBlocking
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
import timber.log.Timber
/**
* A JUnit rule that sets up the API environment for tests based on annotations or instrumentation arguments.
*
* This rule allows tests to specify which API environment to use either through an annotation on the test method/class
* or via an instrumentation argument.
*
* This rule only finds and stores the required API environment. The actual environment setup is performed
* by calling the [setup] method with an appropriate [ApiConfigsManager] instance.
*/
class ApiEnvironmentRule : TestRule {
private var targetEnvironment: ApiEnvironment? = null
/**
* Sets up the API environment based on the provided [ApiConfigsManager].
* This method is typically called in the test setup phase to ensure the correct API environment is configured.
*
* @param apiConfigsManager the manager responsible for API configurations
*/
fun setup(apiConfigsManager: ApiConfigsManager) {
val mutableManager = requireNotNull(apiConfigsManager as? MutableApiConfigsManager) {
"MutableApiConfigsManager isn't available in build type ${BuildConfig.BUILD_TYPE}."
}
mutableManager.setupEnvironment()
}
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
targetEnvironment = determineEnvironment(description)
base.evaluate()
}
}
}
private fun determineEnvironment(description: Description): ApiEnvironment {
val instrumentationArgs = InstrumentationRegistry.getArguments()
val envArg = instrumentationArgs.getString(ENV_ARGUMENT)
if (!envArg.isNullOrEmpty()) return ApiEnvironment.valueOf(envArg.uppercase())
val methodAnnotation = description.getAnnotation(ApiEnv::class.java)
if (methodAnnotation != null) return methodAnnotation.environment
val classAnnotation = description.testClass.getAnnotation(ApiEnv::class.java)
if (classAnnotation != null) return classAnnotation.environment
return ApiEnvironment.MOCK
}
private fun MutableApiConfigsManager.setupEnvironment() {
val environment = requireNotNull(targetEnvironment) { "Target environment is null" }
runBlocking { changeEnvironment(environment) }
Timber.i("API environment set to: ${environment.name}")
}
private companion object {
const val ENV_ARGUMENT = "testEnvironment"
}
}