Search code examples
androidjunitandroid-productflavors

How do I prevent a flavor specific unit test from being compiled in another flavor?


I'm trying to do this, but with a single dimension so it's simpler: How to specify unit test folder in two-dimension flavor

app module build.gradle specifies 2 flavors, free and premium:

    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'

    android {
        compileSdkVersion 28
        defaultConfig {
        applicationId "com.example.myapplication"
        minSdkVersion 15
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner 
    "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android- 
     optimize.txt'), 'proguard-rules.pro'
        }
    }
    flavorDimensions 'someFlavor'
    productFlavors {
        premium {
            applicationIdSuffix '.premium'
        }

        free
    }
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
        debug.java.srcDirs += 'src/debug/kotlin'
        release.java.srcDirs += 'src/release/kotlin'

        free {
            java.srcDirs += 'src/free/kotlin'
            test.java.srcDirs += 'src/test/kotlin'
        }

        premium {
            java.srcDirs += 'src/premium/kotlin'
            test.java.srcDirs += ['src/test/kotlin', 
        'src/testPremium/kotlin']
        }
    }
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.android.support:design:28.0.0'
}

Android Studio's Project view shows:

app
  src
    free
      kotlin (empty folder for now)
    main
      java
        com.example.myapplication
          MainActivity.java
    premium
      kotlin
        com.example.myapplication
          PremiumFile.kt
    test
      kotlin
        com.example.myapplication
          MainTest.kt
    testPremium
      kotlin
        com.example.myapplication
          PremiumTest.kt

PremiumFile.kt

class PremiumFile {
  companion obnject {
    const val PREMIUM_NUM = 10
  }
}

MainTest.kt

class MainTest {
    @Test
    fun basicMainTest() {
      assertEquals(1, 1)
    }
}

PremiumTest.kt

class PremiumTest {
    @Test
    fun premiumTest() {
        assertEquals(PremiumFile.Premium_NUM, 1)
    }
}

As you can see, I lack any unit tests specific to the free flavor. I expected that running ./gradlew compileFreeReleaseUnitTestKotlin would compile the shared unit test inside src/test, but it also compiles the Premium unit tests inside src/testPremium. How can I make sure that PremiumTest.kt is only compiled for Premium tasks such as ./gradlew compilePremiumReleaseUnitTestKotlin?


Solution

  • If you run gradlew sourceSets on your project, you'll get the following section among the others :

    test
    ----
    Compile configuration: testCompile
    build.gradle name: android.sourceSets.test
    Java sources: [app\src\test\java, app\src\test\kotlin, app\src\testPremium\kotlin]
    Java-style resources: [app\src\test\resources]
    
    testPremium
    -----------
    Compile configuration: testPremiumCompile
    build.gradle name: android.sourceSets.testPremium
    Java sources: [app\src\testPremium\java]
    Java-style resources: [app\src\testPremium\resources]
    
    

    As you can see app\src\testPremium\kotlin is in the main test configuration, not in the testPremium. And because test is shared among all the flavors, its sources included in all of them (e.g. testFree configuration will include sources specified in both test and testFree configurations).

    It also suggests you how to fix the issue - use testPremium in the configuration block. So instead of

    premium {
        java.srcDirs += 'src/premium/kotlin' 
        test.java.srcDirs += ['src/test/kotlin', 'src/testPremium/kotlin']
    }
    

    Write

    premium {
        java.srcDirs += 'src/premium/kotlin'
    }
    test{
        java.srcDirs += 'src/test/kotlin'
    }
    testPremium {
        java.srcDirs += 'src/testPremium/kotlin'
    }
    

    So gradlew sourseSets will give you:

    test
    ----
    Compile configuration: testCompile
    build.gradle name: android.sourceSets.test
    Java sources: [app\src\test\java, app\src\test\kotlin]
    Java-style resources: [app\src\test\resources]
    
    testPremium
    -----------
    Compile configuration: testPremiumCompile
    build.gradle name: android.sourceSets.testPremium
    Java sources: [app\src\testPremium\java, app\src\testPremium\kotlin]
    Java-style resources: [app\src\testPremium\resources]
    

    Because test does not exist in AndroidSourceSet, it's not a 'type' of sources like 'java', 'resources' and so on. Instead, test is another instance of AndroidSourceSet. So when you're writing test.java.srcDirs, you are actually configuring this top-level test source set, not tests inside premium.