能否按Flavor或Build Type配置androidTestUtil依赖?
Great question! I've run into similar issues with Test Butler and physical device testing before, and there are a few solid workarounds that don't require modifying your flavor setup or Gradle structure permanently. Let's break them down:
1. Control Test Butler Installation via Gradle Task Conditions
You can keep the androidTestUtil dependency in your Gradle file, but add a condition to skip installing the Test Butler APK when testing on physical devices. This works by targeting the auto-generated install tasks for Test Butler and restricting them to run only on emulators.
Add this to your module-level build.gradle file:
android { // ... your existing config androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.2.0' } tasks.whenTaskAdded { task -> // Target the Test Butler install tasks (names follow the pattern: install[Variant]AndroidTestUtil) if (task.name.startsWith('install') && task.name.endsWith('AndroidTestUtil')) { task.onlyIf { // Option 1: Check if emulator snapshots are enabled (simpler, if you use this feature) android.testOptions.emulatorSnapshots.enabled // Option 2: Detect emulator via adb (more reliable for all scenarios) // def adbOutput = exec { // commandLine 'adb', 'shell', 'getprop', 'ro.product.model' // standardOutput = new ByteArrayOutputStream() // ignoreExitValue = true // }.standardOutput.toString().trim() // adbOutput.contains('emulator') || adbOutput.contains('Android SDK built for') || adbOutput.contains('Genymotion') } } }
When you run tests on a physical device, the Test Butler APK install task will be skipped entirely, so it won't interfere with your test run.
2. Disable Test Butler in Test Code Based on Device Type
If you want a code-level safeguard (regardless of what Gradle does), you can add a check in your test base class to only initialize Test Butler when running on an emulator. This way, even if the Test Butler APK is installed accidentally, it won't be used on physical devices.
Here's an example in Kotlin (adaptable to Java):
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import com.linkedin.testbutler.TestButler import org.junit.AfterClass import org.junit.BeforeClass import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) open class BaseTest { companion object { private fun isRunningOnEmulator(): Boolean { val context = InstrumentationRegistry.getInstrumentation().context val build = android.os.Build return build.MODEL.contains("emulator") || build.MANUFACTURER.contains("Genymotion") || build.BRAND.startsWith("generic") || context.packageManager.hasSystemFeature("android.hardware.type.automotive") } @BeforeClass @JvmStatic fun setupTestButler() { if (isRunningOnEmulator()) { val context = InstrumentationRegistry.getInstrumentation().context TestButler.setup(context) } } @AfterClass @JvmStatic fun teardownTestButler() { if (isRunningOnEmulator()) { val context = InstrumentationRegistry.getInstrumentation().context TestButler.teardown(context) } } } }
All your test classes can extend this base class, and Test Butler will only activate on emulators.
3. Conditionally Include the Test Butler Dependency via Command-Line Property
If you want full control over when Test Butler is included at all, you can use a Gradle project property to toggle the dependency. This way, you don't have to modify the Gradle file—just pass a flag when running emulator tests.
Update your module-level build.gradle dependencies block:
dependencies { // Conditionally add Test Butler only if the property is set if (project.hasProperty("useTestButler")) { androidTestUtil 'com.linkedin.testbutler:test-butler-app:2.2.0' } // ... your other androidTestImplementation and dependencies }
Then run emulator tests with:
./gradlew connectedDebugAndroidTest -PuseTestButler=true
For physical device tests, just run the standard command without the property:
./gradlew connectedDebugAndroidTest
This ensures Test Butler's APK is never even added to the dependency graph when testing on physical devices.
All three approaches let you keep your existing Gradle setup intact while skipping Test Butler for physical device tests. I'd recommend the second option if you want a fail-safe code-level check, or the first if you prefer handling everything in Gradle.
内容的提问来源于stack exchange,提问作者lawonga

