上一篇分析了android项目的测试分类,这一篇讲local单元测试。

参考android官方文档。

测试前需要配置测试环境,新建项目后,目录下会出现app/src/test/java/文件夹,这个文件夹是用于存放local单元测试代码的(就是与androidAPI无关的测试)。

在build.gradle配置文件中增加测试依赖:

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

使用JUnit4测试需要了解下几个基本的注解:

@Before:

使用这个注解可以做一些在测试之间的准备工作。在每一个测试方法之前都会调用这块代码。可以定义多个@Before块,但是多个的调用顺序是不确定的。

@After:

这个注解定义一些测试结束执行的清除工作。在每个测试方法结束后都会调用这块代码。可以定义多个@After块,但是多个的调用顺序是不确定的。

@Test:

这个注解标记测试的方法,一个测试类当中可以定义多个测试方法,每个测试方法都需要使用@Test标记。

@Rule:

使你可以以复用的方式灵活的增加和重新定义每个测试方法的行为。在Android测试中会使用Android Testing Support Library提供的rules,如:ActivityTestRule 或者 ServiceTesteRule。

@BeforeClass:

使用这个注解为测试类设置一个静态方法,一个测试类里面只执行一次。这个方法通常注解一些耗时比较长的操作,如连接数据库。

@AfterClass:

使用这个注解设置一个测试类运行结束后执行的方法。这个方法通常用来释放@BeforeClass里面分配的资源。

@Test(timeout=)

这个注解用来设置一个超时参数,如果测试在设置的时间内没有执行完,就会抛出异常。

另外有时候需要模拟Android的代码,可以使用@Mock注解来模拟。

以下是官网上的一个简答实例:

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(is()和equal())来匹配结果和期望值。

接下来以googlesamples的Android Architecture Blueprints为例,分析下单元测试在项目中的使用。

我取的是todo-mvc-dagger分支的代码,项目有两个Variant(mock和prod)。mock的项目结构如下:

prod的项目结构如下

项目是MVP架构,将业务逻辑从Activity中独立出来,放在presenter里面,对presenter非常适合local单元测试,执行相对比较快。

看下mock下的test里面的TaskDetailPresenterTest测试:

package com.example.android.architecture.blueprints.todoapp.taskdetail;

import com.example.android.architecture.blueprints.todoapp.data.Task;
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource;
import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository; import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; /**
* Unit tests for the implementation of {@link TaskDetailPresenter}
*/
public class TaskDetailPresenterTest { public static final String TITLE_TEST = "title"; public static final String DESCRIPTION_TEST = "description"; public static final String INVALID_TASK_ID = ""; public static final Task ACTIVE_TASK = new Task(TITLE_TEST, DESCRIPTION_TEST); public static final Task COMPLETED_TASK = new Task(TITLE_TEST, DESCRIPTION_TEST, true); @Mock
private TasksRepository mTasksRepository; @Mock
private TaskDetailContract.View mTaskDetailView; /**
* {@link ArgumentCaptor} is a powerful Mockito API to capture argument values and use them to
* perform further actions or assertions on them.
*/
@Captor
private ArgumentCaptor<TasksDataSource.GetTaskCallback> mGetTaskCallbackCaptor; private TaskDetailPresenter mTaskDetailPresenter; @Before
public void setup() {
// Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
// inject the mocks in the test the initMocks method needs to be called.
MockitoAnnotations.initMocks(this); // The presenter won't update the view unless it's active.
when(mTaskDetailView.isActive()).thenReturn(true);
} @Test
public void getActiveTaskFromRepositoryAndLoadIntoView() {
// When tasks presenter is asked to open a task
mTaskDetailPresenter = new TaskDetailPresenter(
ACTIVE_TASK.getId(), mTasksRepository, mTaskDetailView);
mTaskDetailPresenter.start(); // Then task is loaded from model, callback is captured and progress indicator is shown
verify(mTasksRepository).getTask(eq(ACTIVE_TASK.getId()), mGetTaskCallbackCaptor.capture());
InOrder inOrder = inOrder(mTaskDetailView);
inOrder.verify(mTaskDetailView).setLoadingIndicator(true); // When task is finally loaded
mGetTaskCallbackCaptor.getValue().onTaskLoaded(ACTIVE_TASK); // Trigger callback // Then progress indicator is hidden and title, description and completion status are shown
// in UI
inOrder.verify(mTaskDetailView).setLoadingIndicator(false);
verify(mTaskDetailView).showTitle(TITLE_TEST);
verify(mTaskDetailView).showDescription(DESCRIPTION_TEST);
verify(mTaskDetailView).showCompletionStatus(false);
} }

这对任务详细页进行了完整的测试(我只粘贴了一个@Test方法)。在@Before方法里面调用

MockitoAnnotations.initMocks(this);

来模拟@Mock标记的TaskRepository和TaskDetailContract.View。

在@Test测试方法里面,实例化presenter,并且调用start方法,使用mockito的verify方法来校验是否按预期的调用了TaskRepository和TaskDetailContract.View里面的方法。

googlesamples里面有详尽完整的测试,路漫漫,其修远兮,囧囧囧囧。

Android测试:从零开始2——local单元测试的更多相关文章

  1. android测试的相关概念以及单元测试

    1.测试的相关概念  1.根据是否知道源代码分类: 黑盒测试: a - b - c  边值测试    白盒测试: 根据源代码写测试方法 或者 测试用例; 2.根据测试的粒度分类: 方法测试:写完一个方 ...

  2. Android测试:Building Local Unit Tests

    原文:https://developer.android.com/training/testing/unit-testing/local-unit-tests.html 如果你的单元测试没有依赖或者只 ...

  3. Android测试:从零开始1——简介

    参考文档:https://developer.android.com/training/testing/start/index.html 测试分类 使用android studio进行测试,首先需要先 ...

  4. Android测试(四):Instrumented 单元测试

    原文:https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html Instrume ...

  5. 【Android测试】【第十五节】Instrumentation——官方译文

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/5482207.html 前言 前面介绍了不少Android ...

  6. 【Android测试】【第十一节】Uiautomator——简介

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4872244.html 前言 在App的测试中,除了单元测试 ...

  7. Android studio下gradle Robolectric单元测试配置

    android studio下gradle Robolectric单元测试配置 1.Robolectric Robolectric是一个基于junit之上的单元测试框架.它并不依赖于Android提供 ...

  8. 【Android测试】【第九节】MonkeyRunner—— 初识

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处. 转载请注明出处:http://www.cnblogs.com/by-dream/p/4836815.html 不得不说两句,过了这么久才再次更新博 ...

  9. 【Android测试】【第一节】ADB——初识和用法

    ◆版权声明:本文出自胖喵~的博客,转载必须注明出处.  转载请注明出处:http://www.cnblogs.com/by-dream/p/4630046.html 写在前面的话 感觉自己进入Andr ...

随机推荐

  1. 转:Mosquitto配置----日志设置

    1.mosquitto的日志输出方式简介 mosquitto是一个纯C的代码,它的日志输出支持若干中日志输出方式,通过修改配置项:log_dest即可完成对各种日志输出类型的切换,常见的日志输出类型有 ...

  2. mosquitto---config.mk

    mosquitto安装时在解压压缩包后生成的文件夹中我们可以找到mosquitto主要配置文件config.mk 这个文件的主要内容: # 是否支持tcpd/libwrap功能. #WITH_WRAP ...

  3. T-sql for xml path使用(转)

    参考: http://www.cnblogs.com/langhua/p/4193161.html //用法: FOR XML PATH 方法是用于将查询结果集以XML形式展示 sql: p.Cont ...

  4. unity, editorWindow update计时

    对于editorWindow,Time.deltaTime不起作用,所以需用下面方法对update进行计时: public class myEditorWindow : EditorWindow{ p ...

  5. atitit. 文件上传带进度条 atiUP 设计 java c# php

    atitit. 文件上传带进度条 atiUP 设计 java c# php 1. 设计要求 1 2. 原理and 架构 1 3. ui 2 4. spring mvc 2 5. springMVC.x ...

  6. 每日英语:Air Pollution From Coal Use Cuts Lifespans in China, Study Shows

    Air pollution from coal combustion likely cut life expectancy in parts of China by more than five ye ...

  7. HDU 1863 畅通工程 克鲁斯卡尔算法

    畅通工程 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  8. 【ActiveMQ】ActiveMQ在Windows的安装,以及点对点的消息发送案例

    公司最近会用MQ对某些业务进行处理,所以,这次我下载了apache-activemq-5.12.0-bin把玩下. 基于练习方便需要,使用Windows的版本. 参考的优秀文章: activemq的几 ...

  9. Ubuntu 下添加OpenERP command 快捷启动方式

    编辑home目录下的.bashrc文件 alias xjerp="~/odoo/xj/openerp-server -r openerp --addons-path='~/odoo/xj/o ...

  10. Ubuntu 12.04 Subversion及GUI客户端RabbitVCS安装

    (经过一天的使用,发现pygtk的内存泄漏问题严重影响使用,需要打一下deepin ui做的补丁:https://github.com/linuxdeepin/deepin-ui) 1. 类似Tort ...