Last active
February 14, 2018 19:52
-
-
Save zawadz88/9e2c3e7b109e11b59536cfbe74778b6d to your computer and use it in GitHub Desktop.
Multi-flavored UI test filtering
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.stepstone.base.test.runner.filter | |
| import android.os.Bundle | |
| import com.stepstone.base.BuildConfig | |
| import com.stepstone.base.test.runner.annotation.TestFilter | |
| import org.junit.runner.Description | |
| import timber.log.Timber | |
| /** | |
| * Filter used to determine if a test should be executed or not. | |
| * | |
| * This does the following: | |
| * | |
| * - runs all tests if 'class' bundle argument was provided to the runner (this means that either Android Test Orchestrator wants to run a single test or that we want to run a test from Android Studio directly) | |
| * - checks if [TestFilter] is present on a test method -> if so it filters the test based on that annotation | |
| * - checks if [TestFilter] is present on a test class -> if so it filters the test based on that annotation | |
| * - otherwise it runs the test | |
| * | |
| * | |
| * [TestFilter] filtering runs the following checks in order: | |
| * | |
| * - if brand is not present in [TestFilter.brands] it will not run the test | |
| * - if brand is present in [TestFilter.notBrands] it will not run the test | |
| * | |
| * To run a test on a single flavor e.g. flavor1 you need to add [TestFilter] to a test method like this: | |
| * | |
| * `@TestFilter(brands = [FLAVOR_1])` | |
| * | |
| * To run a test on all brands but one (flavor4) you need to add [TestFilter] to a test method like this: | |
| * | |
| * `@TestFilter(notBrands = [FLAVOR_4])` | |
| * | |
| * @see org.junit.runner.manipulation.Filter | |
| */ | |
| class BrandFilter(bundle: Bundle) : ParentFilter() { | |
| companion object { | |
| private const val CLASS_BUNDLE_KEY = "class" | |
| } | |
| private val shouldFilterTests: Boolean | |
| init { | |
| Timber.i("BrandFilter bundle: %s", bundle) | |
| val listTestsForOrchestrator = java.lang.Boolean.parseBoolean(bundle.getString("listTestsForOrchestrator", "false")) | |
| val runsSingleClass = bundle.containsKey(CLASS_BUNDLE_KEY) | |
| shouldFilterTests = listTestsForOrchestrator && !runsSingleClass | |
| } | |
| override fun evaluateTest(description: Description): Boolean { | |
| if (!shouldFilterTests) { | |
| return true | |
| } | |
| val classTestFilter = description.testClass.getAnnotation(TestFilter::class.java) | |
| val testFilter = description.getAnnotation(TestFilter::class.java) | |
| if (testFilter != null) { | |
| return evaluateTestWithFilter(testFilter) | |
| } else if (classTestFilter != null) { | |
| return evaluateTestWithFilter(classTestFilter) | |
| } | |
| return true | |
| } | |
| private fun evaluateTestWithFilter(testFilter: TestFilter): Boolean { | |
| val brands = testFilter.brands | |
| val notBrands = testFilter.notBrands | |
| return (brands.contains(BuildConfig.FLAVOR) | |
| && !notBrands.contains(BuildConfig.FLAVOR)) | |
| } | |
| override fun describe(): String { | |
| return "Brand filter" | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.stepstone.base.test.runner; | |
| import android.os.Bundle; | |
| import android.support.test.runner.AndroidJUnitRunner; | |
| import com.stepstone.base.test.runner.filter.BrandFilter; | |
| public class MyAndroidJUnitRunner extends AndroidJUnitRunner { | |
| private static final String FILTER_BUNDLE_KEY = "filter"; | |
| @Override | |
| public void onCreate(final Bundle bundle) { | |
| bundle.putString(FILTER_BUNDLE_KEY, SCBrandFilter.class.getName()); | |
| super.onCreate(bundle); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.stepstone.base.test.runner.filter | |
| import org.junit.runner.Description | |
| import org.junit.runner.manipulation.Filter | |
| /** | |
| * Helper parent class for [Filter] that allows suites to run if any child matches. | |
| */ | |
| abstract class ParentFilter : Filter() { | |
| /** | |
| * {@inheritDoc} | |
| */ | |
| override fun shouldRun(description: Description): Boolean { | |
| if (description.isTest) { | |
| return evaluateTest(description) | |
| } | |
| // this is a suite, explicitly check if any children should run | |
| // no children to run, filter this out | |
| return description.children.any { shouldRun(it) } | |
| } | |
| /** | |
| * Determine if given test description matches filter. | |
| * | |
| * @param description the [Description] describing the test | |
| * @return `true` if matched | |
| */ | |
| protected abstract fun evaluateTest(description: Description): Boolean | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.stepstone.base.test.runner.annotation | |
| /** | |
| * Used for narrowing down on which brands any given test/test suite should be executed. | |
| * | |
| * @see com.stepstone.base.test.runner.filter.BrandFilter | |
| */ | |
| @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) | |
| @Retention(AnnotationRetention.RUNTIME) | |
| annotation class TestFilter( | |
| val brands: Array<String> = [ | |
| FLAVOR_1, FLAVOR_2, FLAVOR_3, FLAVOR_4], | |
| val notBrands: Array<String> = []) | |
| const val FLAVOR_1 = "flavor1" | |
| const val FLAVOR_2 = "flavor2" | |
| const val FLAVOR_3 = "flavor2" | |
| const val FLAVOR_4 = "flavor2" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment