构建本地单元测试

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

配置测试环境

在您的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'
}

创建一个本地单元测试类

你的本地单元测试类应该写成一个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()方法)将返回的结果与预期结果进行匹配。

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/零返回值可以在测试中引入回归,这难以调试,并且可能允许失败的测试通过。 只能使用它作为最后的手段。

运行本地单元测试

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,361评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 一、百变怪 Mockito Mockito可谓是Java世界的百变怪,使用它,可以轻易的复制出各种类型的对象,并与...
    罗力阅读 3,898评论 3 18
  • 回顾: 《谈谈为什么写单元测试》 基本单元测试框架 Java单元测试框架:Junit、Mockito、Powerm...
    键盘男阅读 50,503评论 19 160
  • 姚安的天气总是那么好,是吧。这种感觉很舒服。 世界上最大的激励莫过于你去努力之后发现原来看起来很难的事情你也可以做...
    稻城禾欢阅读 148评论 0 0