构建本地单元测试

伴随JUnit中 TemporaryFolder @Rule 的出现,测试文件和目录变得简单了。

如果你的单元测试没有依赖或者只有Android上的简单依赖,你应该在本地开发机器上运行测试。
这种测试方法是高效的,因为它可以帮助您避免每次运行测试时将目标应用程序和单元测试代码加载到物理设备或模拟器上的开销。
因此,运行单元测试的执行时间大大减少。
使用这种方法,通常使用模拟框架(如Mockito)来实现任何依赖关系。

在 JUnit
中,规则(@Rule)可作为构造测试用具(fixture)时初始化方法和清理方法的替代和补充(在
JUnit
中,这2种方法分别通过以下注解标注:org.junit.Beforeorg.junit.Afterorg.junit.BeforeClass
org.junit.AfterClass
。而且规则的功能更加强大并且也更易于在项目和类之间共享。

配置测试环境

在您的Android
Studio项目中,必须将用于本地单元测试的源文件存储在module-name / src /
test / java /。 创建新项目时,此目录已存在。

您还需要为项目配置测试依赖关系,以使用JUnit 4框架提供的标准API。
如果您的测试需要与Android依赖关系交互,请包括Mockito库以简化本地单元测试。
要了解有关在本地单元测试中使用模拟对象的更多信息,请参阅下面的Mocking
Android dependencies。

在应用程序的顶级build.gradle文件中,您需要将这些库指定为依赖关系:

dependencies {
    // Required -- JUnit 4 framework
    testCompile 'junit:junit:4.12'
    // Optional -- Mockito framework
    testCompile 'org.mockito:mockito-core:1.10.19'
}

译者注:测试用具是指作为测试运行基准的一组对象所呈现的一个稳定状态。其目的是确保测试是运行在一个众所周知的、稳定的环境中的,以实现测试的可重复执行。准备输入数据、生成模拟对象(Mock)、将特定的数据加载到数据库、复制一组特定的文件等,这些都属于构造测试用具。

创建一个本地单元测试类

你的本地单元测试类应该写成一个JUnit 4测试类。
JUnit是Java最受欢迎和广泛使用的单元测试框架。 这个框架的最新版本,JUnit
4,允许你以比它的前任版本更清洁和更灵活的方式编写测试。
与以前的基于JUnit 3的Android单元测试的方法不同,使用JUnit
4,您不需要扩展junit.framework.TestCase类。
您也不需要在测试方法名称前加上’test’关键字,或者使用junit.framework或junit.extensions包中的任何类。

要创建基本的JUnit 4测试类,请创建一个包含一个或多个测试方法的Java类。
测试方法以@Test注释开始,包含练习和验证要测试的组件中的单个功能的代码。

以下示例显示如何实现本地单元测试类。
测试方法emailValidator_CorrectEmailSimple_ReturnsTrue验证被测应用程序中的isValidEmail()方法是否返回正确的结果。

以下示例显示如何实现本地单元测试类。
测试方法emailValidator_CorrectEmailSimple_ReturnsTrue验证被测应用程序中的isValidEmail()方法是否返回正确的结果。

import org.junit.Test;
import java.util.regex.Pattern;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

public class EmailValidatorTest {

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

要测试应用程序中的组件是否返回预期结果,请使用junit.Assert方法执行验证检查(或断言),以便将受测试组件的状态与某些预期值进行比较。
为了使测试更可读,可以使用Hamcrest
matchers(例如is()和equalTo()方法)将返回的结果与预期结果进行匹配。

待测试的代码

public void writeTo(String path, String content) throws IOException {
    Path target = Paths.get(path);
    if (Files.exists(target)) {
        throw new IOException("file already exists");
    }
    Files.copy(new ByteArrayInputStream(content.getBytes("UTF8")), target);
}

Mock Android dependencies

默认情况下,Gradle的Android插件针对android.jar库的修改版本执行本地单元测试,该版本不包含任何实际代码。
相反,从单元测试中调用Android类的方法会抛出异常。
这是为了确保你只测试你的代码,不依赖于Android平台的任何特定的行为(当没有明确模拟时)。

您可以使用模拟框架在代码中存根外部依赖关系,以便轻松测试您的组件是否按照预期的方式与依赖关系交互。
通过用模拟对象替换Android依赖项,您可以将单元测试与Android系统的其余部分隔离,同时验证这些依赖关系中的正确方法是否被调用。
Java的Mockito
mocking框架(1.9.5及更高版本)提供了与Android单元测试的兼容性。
使用Mockito,您可以配置模拟对象以在调用时返回一些特定值。

要使用此框架将mock对象添加到本地单元测试,请遵循以下编程模型:
1>
在build.gradle文件中包含Mockito库依赖关系,如配置测试环境中所述。
2>
在单元测试类定义的开始,添加@RunWith(MockitoJUnitRunner.class)注释。
这个注释告诉Mockito测试运行器验证你的框架的使用是正确的,并简化了你的模拟对象的初始化。
3> 要为Android依赖项创建模拟对象,请在字段声明之前添加@Mock注释。
4>
要存根依赖关系的行为,可以使用when()和thenReturn()方法指定条件并返回值。

以下示例显示如何创建使用mock Context对象的单元测试。

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import android.content.SharedPreferences;

@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));
    }
}

要了解有关使用Mockito框架的更多信息,请参阅Mockito API
reference和示例代码中的SharedPreferencesHelperTest类。

如果Android.jar中的Android
API抛出的异常对于测试有问题,您可以更改行为,以使方法通过在项目的顶级build.gradle文件中添加以下配置来返回null或零:

android {
  ...
  testOptions {
    unitTests.returnDefaultValues = true
  }
}

警告:将returnDefaultValues属性设置为true应该小心。
null/零返回值可以在测试中引入回归,这难以调试,并且可能允许失败的测试通过。
只能使用它作为最后的手段。

测试类

public class FileWriterTest {

    private FileWriter fileWriter = new FileWriter();

    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Rule
    public ExpectedException thrown = ExpectedException.none();

    @Test
    public void throwsErrorWhenTargetFileExists() throws IOException {
        // arrange
        File output = temporaryFolder.newFile("output.txt");

        thrown.expect(IOException.class);
        thrown.expectMessage("file already exists");

        // act
        fileWriter.writeTo(output.getPath(), "test");
    }

    @Test
    public void writesContentToFile() throws IOException {
        // arrange
        File output = temporaryFolder.newFolder("reports")
                .toPath()
                .resolve("output.txt")
                .toFile();

        // act
        fileWriter.writeTo(output.getPath(), "test");

        // assert
        assertThat(output)
                .hasContent("test")
                .hasExtension("txt")
                .hasParent(resolvePath("reports"));
    }

    private String resolvePath(String folder) {
        return temporaryFolder
                .getRoot().toPath()
                .resolve(folder)
                .toString();
    }
}

译者注:第35行的 assertThat() 是类
org.assertj.core.api.Assertions 中的静态方法。

TemporaryFolder 提供了2个方法 newFile
newFolder,分别用于管理文件和目录。这2个方法都可以返回所需要的对象。返回的文件或目录都是由
setup
方法创建的并被存放在临时目录中。要想获取临时目录自身的路径,可以使用
TemporaryFoldergetRoot 方法。

无论测试成功与否,任何在测试过程中添加到临时目录的文件或目录都会在测试结束时删除。

本示例可以从我在 GitHub 上的项目
unit-testing-demo
中找到,除此之外该项目中还有很多其他示例可供诸位参考。

运行本地单元测试

要运行本地单元测试,请按照下列步骤操作:
1> 通过单击工具栏中的Sync Project,确保您的项目与Gradle同步。
2> 使用以下方法之一运行测试:
a> 要运行单个测试,请打开“
Project”窗口,然后右键单击测试,然后单击Run。
b>
要测试类中的所有方法,请右键单击测试文件中的类或方法,然后单击Run。
c> 要在目录中运行所有测试,请右键单击目录并选择Run tests。
Gradle的Android插件编译位于默认目录(src / test / java
/)中的本地单元测试代码,构建一个测试app,并使用默认的测试运行器类在本地执行它。
然后,Android Studio将在“ Run”窗口中显示结果。

发表评论

电子邮件地址不会被公开。 必填项已用*标注