Android测试:从零开始2——local单元测试

上一篇分析了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("[email protected]"), 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里面有详尽完整的测试,路漫漫,其修远兮,囧囧囧囧。

时间: 2024-10-08 09:29:53

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

Android测试(四):Local 单元测试

Android测试(四):Local 单元测试 发布时间 2017年12月20日 虫师 原文:https://developer.android.com/training/testing/unit-testing/local-unit-tests.html 如果你的单元测试没有依赖或者只有简单的Android依赖,则应该在本地开发机器上运行测试.这种测试方法非常高效,因为它可以帮助你避免每次运行测试时将目标应用程序和单元测试代码加载到真机或模拟器上的开销.因此,运行单元测试的执行时间大大减少了.

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

参考文档:https://developer.android.com/training/testing/start/index.html 测试分类 使用android studio进行测试,首先需要先了解android测试的分类,新建工程后,项目会默认包含两个测试目录: 1.本地单元测试(Local unit tests) 测试代码位于module-name/src/test/java/,这些测试直接运行在本地JVM上,不需要使用Android框架的API. 2.设备测试(Instrumente

Android测试(三):Android 单元测试

Android测试(三):Android 单元测试 发布时间 2017年12月20日 虫师 原文:https://developer.android.com/training/testing/unit-testing/index.html 单元测试是你的应用程序测试策略的基本测试. 通过针对您的代码创建和运行单元测试,你可以轻松验证各个单元的逻辑是否正确. 在每次构建之后运行单元测试可帮助你快速捕获并修复由代码更改引入到应用程序的软件回归. 单元测试通常以可重复的方式实现尽可能小的代码单元(可以

(4.5.4)Android测试TestCase单元(Unit test)测试和instrumentationCase单元测试

Android单元和instrumentation单元测试 Developing Android unit and instrumentation tests Android的单元测试是基于JUnit的.可分为: 1.本地单元测试 - 可以在JVM上运行测试(速度快,优先考虑). 2.Instrumented单元测试 - 需要Android系统 Android的Gradle插件支持在JVM上执行Andr??oid单元测试.它使用特殊版本的android.jar(也称为 Android mocka

Android测试(五):Instrumented 单元测试

Android测试(五):Instrumented 单元测试 发布时间 2017年12月20日 虫师 原文:https://developer.android.com/training/testing/unit-testing/instrumented-unit-tests.html Instrumented 单元测试是在真机和模拟器上运行的测试,它可以利用Android框架API和支持的API(如Android测试支持库).如果你的测试需要访问工具信息(例如目标应用程序的Context),或者

【Android测试】【第一节】初识ADB

● 前言 利用工作之余的时间将Android测试相关知识进行一个整理,一来巩固一下自己学到的东西:二来 可以让想学习Android测试的同学多一份学习资料. ● 何为ADB 什么是ADB呢?为什么手机和电脑之间用usb连接后,就可以电脑上通过应用宝或者豌豆荚等手机助 手软件对手机进行操作? 答案就是:因为这些都依赖于ADB.ADB就是连接手机和电脑通信的桥梁,只要这个桥梁建立起了, 那么你就可以通过电脑来控制你的手机了. ADB全称Android Debug Bridge,我把它解释为"安卓调试

2014 非常好用的开源 Android 测试工具

2014 非常好用的开源 Android 测试工具 - 开源中国社区 当前有很大的趋势是转向移动应用平台,Android 是最广泛使用的移动操作系统,2014 年大约占 80% 以上的市场.在开发 Android 应用的时候要进行测试,现在市场上有大量的测试工具. 本文主要是展示一系列的开源 Android 测试工具.每个工具都会有相应的简短介绍,还有一些相关的资源.Android 测试工具列表是按照字母来排序的,最后还会介绍几个不是特别活跃的 Android 测试相关的开源项目. 本文提到的开

Android 测试 Appium、Robotium、monkey等框架或者工具对比

1. Appium测试 (功能测试,用户接受度测试,黑盒测试) - Rating: 8 Website: http://appium.io/ Appium测试相当于黑盒测试.只是测试UI逻辑正确性.所以Appium测试框架提供的方法有限.获取一个AppiumDriver对象.该对象只是有很多findElements()的方法,获取到UI元素.UI元素是WebElement,这个类提供的方法基本是获取信息为主,比如获取name,class,tagName,location,text,isSlect

Android测试工具ThreadingTest开放API接口说明

ThreadingTest(简称TT)第一期是一款Android白盒测试工具,使用离线检测的方式,在保护用户源代码的基础上,运用插装.第五代覆盖率等技术,为开发工程师与测试工程师提供一套高效可量化.可视化的交流工具.对比其它测试工具,TT在自动化测试时,会对应测试用例自动生成测试用例和代码之间的关系以及函数覆盖率,并且以TT自带的双向追溯图进行展示,在整个自动化测试进行过程中,TT还会以示波器界面可视化的监控整个自动化测试中每时每刻获取的测试数据. 基于其它测试工具测试时,TT还开放了API接口