Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to write a unit test for a target API

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...
    }
}
like image 799
Mark Lapasa Avatar asked Dec 30 '15 15:12

Mark Lapasa


1 Answers

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

  • http://www.netmite.com/android/mydroid/development/pdk/docs/instrumentation_testing.html
  • https://github.com/googlesamples/android-testing-templates/blob/master/AndroidTestingBlueprint

Hope it help

like image 193
piotrek1543 Avatar answered Oct 04 '22 00:10

piotrek1543