11

I am in the process of writing Android instrumented tests in an area were Robolectric custom shadows fall short.

Ideally, we want to write code that is flexible across all versions of the Android SDK however I am in an edge case situation where I need to write a individual unit test for a method that works only Marshmallow.

I also need to write a unit test that only works for Lollipop and under because of differences in operating system dependencies (i.e. java.lang.NoClassDefFoundError's)

Is there anyway I can do this through Junit4-like annotations or something similar to what Robolectric does where it can ignore running the tests if the SDK is not a good fit?

I do not want to be writing code like this:

// MarshmallowWidgetTest.java
@Test
public void testPrintName()
{
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
    {
        // ...asserts...
    }
}

// LollipopWidgetTest.java
@Test
public void testPrintName()
{
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP)
    {
        // ...asserts...
    }
}
Mark Lapasa
  • 1,644
  • 4
  • 19
  • 37

1 Answers1

11

I'm not very familiar with unit testing or Robolectric, but because at time of writing unit tests by me there was no support for API 23 I used that config:

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21) //this guy
public class MainActivityTest {

    MainActivity_ activity = Robolectric.setupActivity(MainActivity_.class);

}

So like you see there's a annotation which you can use to your test classes.


EDIT:

Sorry that I focused only on Robolectric test framework, not main problem.

For annotating instrumentation tests for specific API I would use:

1. Class with @Before annotation

Create a class with @Before annotation, where it would check the API of tested devices. If wrong, the tests would fail in this method. Use fail(); method.

2. Use @SdkSuppress annotation

Indicates that a specific test or class requires a minimum API Level to execute.

Test(s) will be skipped when executed on android platforms less than specified level.

From: http://developer.android.com/reference/android/support/test/filters/SdkSuppress.html

So if you would set @SdkSuppress(minSdkVersion=23) it would run only on Android Marshmallow devices and if @@SdkSuppress(minSdkVersion=20) it would run only on higher 5.0 API devices.

Read also: http://www.vogella.com/tutorials/AndroidTesting/article.html

3. Create your own annotation like @SdkOnly

Maybe this article would be useful: http://help.testdroid.com/customer/portal/articles/1256803-using-annotations-in-android-instrumentation-tests

4. Create suites for your specific instrumentation tests

For this purpose you would use @RunWith() and Suites.SuiteClasses() annotations.

To organize the execution of your instrumented unit tests, you can group a collection of test classes in a test suite class and run these tests together. Test suites can be nested; your test suite can group other test suites and run all their component test classes together.

A test suite is contained in a test package, similar to the main application package. By convention, the test suite package name usually ends with the .suite suffix (for example, com.example.android.testing.mysample.suite).

To create a test suite for your unit tests, import the JUnit RunWith and Suite classes. In your test suite, add the @RunWith(Suite.class) and the @Suite.SuitClasses() annotations. In the @Suite.SuiteClasses() annotation, list the individual test classes or test suites as arguments.

The following example shows how you might implement a test suite called UnitTestSuite that groups and runs the CalculatorInstrumentationTest and CalculatorAddParameterizedTest test classes together.

import com.example.android.testing.mysample.CalculatorAddParameterizedTest;
import com.example.android.testing.mysample.CalculatorInstrumentationTest;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

// Runs all unit tests.
@RunWith(Suite.class)
@Suite.SuiteClasses({CalculatorInstrumentationTest.class,
        CalculatorAddParameterizedTest.class})
public class UnitTestSuite {}

From: http://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html

5. Helpful resources

Hope it help

piotrek1543
  • 19,130
  • 7
  • 81
  • 94
  • 3
    `Config` is applicable for methods as well – Eugen Martynov Dec 30 '15 at 19:32
  • If you feel this is a correct answer, please help others to find it and mark it. Remember, if there would be better one, you would change it ;-) – piotrek1543 Dec 30 '15 at 19:41
  • 1
    Yeah, it's not the correct answer because I am asking about Android instrumented tests not Roblectric JVM Unit Tests. Your answer refers to something I am already ware about when I say "something similar to what Robolectric does where it can ignore running the tests if the SDK is not a good fit" The ideal solution would be some magical annotation using @RunWith(AndroidJUnit4.class) test runner, not @RunWith(RobolectricGradleTestRunner.class) – Mark Lapasa Dec 30 '15 at 20:00
  • 1
    i've edited my answer. I think @SdkSupress annotation might help you to solve your problem ;-) – piotrek1543 Dec 30 '15 at 20:41
  • 1
    This is absolutely exactly what I needed! SdkSuppress doesn't show up anywhere on Stackoverflow so this is new knowledge. Thanks for your help! – Mark Lapasa Jan 02 '16 at 10:54
  • 1
    Just as an added note to anyone else who tries to use @SdkSupress, the annotation will only work in an Android instrumented test i.e. under /androidTest. It will not show up in a JVM Robolectric test i.e. under /test – Mark Lapasa Jan 02 '16 at 10:57