美文网首页
Android单元测试

Android单元测试

作者: sylcrq | 来源:发表于2016-10-26 16:15 被阅读92次

关键词:Android Testing, JUnit, Mockito, Espresso, Robolectric

Android单元测试可以分为两类:

  • Local unit tests

    1. 测试代码位于module-name/src/test/java/目录下
    2. 运行在本地JVM上
  • Instrumented tests

    1. 测试代码位于module-name/src/androidTest/java/目录下
    2. 必须运行在Android真机或模拟器上
Android Test

JUnit 4 注解

  • @Before: Use this annotation to specify a block of code that contains test setup operations. The test class invokes this code block before each test. You can have multiple @Before methods but the order in which the test class calls these methods is not guaranteed.
  • @After: This annotation specifies a block of code that contains test tear-down operations. The test class calls this code block after every test method. You can define multiple @After operations in your test code. Use this annotation to release any resources from memory.
  • @Test: Use this annotation to mark a test method. A single test class can contain multiple test methods, each prefixed with this annotation.
  • @Rule: Rules allow you to flexibly add or redefine the behavior of each test method in a reusable way. In Android testing, use this annotation together with one of the test rule classes that the Android Testing Support Library provides, such as ActivityTestRule
    or ServiceTestRule
    .
  • @BeforeClass: Use this annotation to specify static methods for each test class to invoke only once. This testing step is useful for expensive operations such as connecting to a database.
  • @AfterClass: Use this annotation to specify static methods for the test class to invoke only after all tests in the class have run. This testing step is useful for releasing any resources allocated in the @BeforeClass block.
  • @Test(timeout=): Some annotations support the ability to pass in elements for which you can set values. For example, you can specify a timeout period for the test. If the test starts but does not complete within the given timeout period, it automatically fails. You must specify the timeout period in milliseconds, for example: @Test(timeout=5000).

1. Local Unit Tests

build.gradle中配置测试依赖:

dependencies {
    ......
    testCompile 'junit:junit:4.12'
    testCompile 'org.robolectric:robolectric:3.0'
    testCompile 'org.hamcrest:hamcrest-all:1.3'
    testCompile 'org.mockito:mockito-core:1.10.19'
}

一个简单的例子:

public class EmailValidatorTest {
    @Test
    public void emailValidator_CorrectEmailSimple_ReturnsTrue() {
        assertThat(EmailValidator.isValidEmail("name@email.com"), is(true));
    }
    ...
}

By default, the Android Plug-in for Gradle executes your local unit tests against a modified version of the android.jar library, which does not contain any actual code. Instead, method calls to Android classes from your unit test throw an exception.

使用 Mockito mock 测试代码中对Android的依赖:

@RunWith(MockitoJUnitRunner.class)
public class UnitTestSample {
    private static final String FAKE_STRING = "HELLO WORLD";
    @Mock
    Context mMockContext;
    @Test
    public void readStringFromContext_LocalizedString() {
        // Given a mocked Context injected into the object under test...
        when(mMockContext.getString(R.string.hello_word))
                .thenReturn(FAKE_STRING);
        ClassUnderTest myObjectUnderTest = new ClassUnderTest(mMockContext);
        // ...when the string is returned from the object under test...
        String result = myObjectUnderTest.getHelloWorldString();
        // ...then the result should be the expected one.
        assertThat(result, is(FAKE_STRING));
    }
}

Robolectric

Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds.

  • 配置Robolectric
testCompile "org.robolectric:robolectric:3.1.4"
@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class SandwichTest {
}

Note that you must specify the constants field which points to the BuildConfig.class generated by the build system. Robolectric uses the constants in the class to compute the output paths used by Gradle when building your project. Without these values, Robolectric will not be able to find your merged manifest, resources, or assets.

一个简单的例子:

@RunWith(RobolectricTestRunner.class)
public class WelcomeActivityTest {

    @Test
    public void clickingLogin_shouldStartLoginActivity() {
        WelcomeActivity activity = Robolectric.setupActivity(WelcomeActivity.class);
        activity.findViewById(R.id.login).performClick();

        Intent expectedIntent = new Intent(activity, LoginActivity.class);
        assertThat(shadowOf(activity).getNextStartedActivity()).isEqualTo(expectedIntent);
    }
}

The primary way to customize Robolectric is done via the @Config annotation.

  1. Configure SDK Level
    Robolectric 默认会运行 manifest 中指定的targetSdkVersion版本,如果某些代码需要在不同的SDK版本下测试,可以如下设置:
@Config(sdk = Build.VERSION_CODES.JELLY_BEAN)
public class SandwichTest {

    @Config(sdk = Build.VERSION_CODES.KITKAT)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}
  1. Configure Application Class
    Robolectric 默认会创建 manifest 中指定的Application类,如果需要替换为其他自定义,可以如下设置:
@Config(application = CustomApplication.class)
public class SandwichTest {

    @Config(application = CustomApplicationOverride.class)
    public void getSandwich_shouldReturnHamSandwich() {
    }
}
  1. Configure Resource Paths
    自定义manifest,resource和assets目录的路径,如果你使用的是自定义build system的话,会需要用到。

  2. Config Properties
    上述的配置既可以在@Config注解中指定,也可以写在配置文件中。
    Create a file named robolectric.properties and make sure it can be found on the classpath.

  3. System Properties

android {
  testOptions {
    unitTests.all {
      systemProperty 'robolectric.dependency.repo.url', 'https://local-mirror/repo'
      systemProperty 'robolectric.dependency.repo.id', 'local'
    }
  }
}
  • 关于Activity生命周期
    创建一个MyAwesomeActivity的实例,并调用 life cycle 的onCreate()方法:
Activity activity = Robolectric.buildActivity(MyAwesomeActivity.class).create().get();
  • 测试Qualified Resources
    resource qualifiers允许根据设备不同的语言、屏幕大小等情况加载不同的资源
    values/strings.xml
<string name="not_overridden">Not Overridden</string>
<string name="overridden">Unqualified value</string>
<string name="overridden_twice">Unqualified value</string>

values-en/strings.xml

<string name="overridden">English qualified value</string>
<string name="overridden_twice">English qualified value</string>

values-en-port/strings.xml

<string name="overridden_twice">English portrait qualified value</string>
@Test
@Config(qualifiers="en-port")
public void shouldUseEnglishAndPortraitResources() {
  final Context context = RuntimeEnvironment.application;
  assertThat(context.getString(R.id.not_overridden)).isEqualTo("Not Overridden");
  assertThat(context.getString(R.id.overridden)).isEqualTo("English qualified value");
  assertThat(context.getString(R.id.overridden_twice)).isEqualTo("English portrait qualified value");
}
  • Shadow Classes

Robolectric defines many shadow classes, which modify or extend the behavior of classes in the Android OS. When an Android class is instantiated, Robolectric looks for a corresponding shadow class, and if it finds one it creates a shadow object to associate with it. Every time a method is invoked on an Android class, Robolectric ensures that the shadow class' corresponding method is invoked first (if there is one), so it has a chance to work its magic. This applies to all methods, even static and final methods, because Robolectric is extra tricky!

2. Instrumented Unit Tests

build.gradle中配置测试依赖:

android {
    ...
    defaultConfig {
        ...
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    ...
    androidTestCompile 'com.android.support:support-annotations:24.0.0'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    // Optional -- Hamcrest library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // Optional -- UI testing with Espresso
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
    // Optional -- UI testing with UI Automator
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}

参考资料:

相关文章

网友评论

      本文标题:Android单元测试

      本文链接:https://www.haomeiwen.com/subject/bmnduttx.html