App单元测验

我开发经验比较少,这公司也不算太靠谱,由于经验尚浅,很多代码有的时候也有不少毛毛糙糙的地方,没测试,那就自己撸起袖管上吧。本文小记一下我看别人的文所得和一些翻译。

做开发这么长时间了,对app测试一直没有深入研究过。平时开发完后,都是自己根据原型操作一下,没有太大问题就交给其他人测试了,现在要专门研究一下测试这块的东西,包括自动化测试、单元测试等方面 。

Building Effective Unit Tests 这个是原文地址,想看原文的可以自己看一下,下面是我自己的翻译= =,比较渣。

使用Android Studio进行单元测试

android studio的单元测试由于只是模拟android开发环境,但是其不是真正的android开发环境,所以不能测试UI功能,不能测试需要硬件支持的功能(比如蓝牙,wifi等),不能测试App跳转等等,那么其可以测试那些内容呢?

  • 测试一些数据性的功能,比如加载网络数据
  • 测试SharedPerferences,测试数据库,测试函数等
  • 工具类的测试,比如验证时间,转化格式,正则验证等等

在Android Studio中进行单元测试并不需要什么插件或者过多的配置,Android Studio本身就集成了测试环境,无论是单纯的java代码单元测试还是依赖Android SDK的Android代码单元测试都可以。


Android单元测试框架主要有:JUnit和Robolectric,本文就不详细介绍。
1. 使用JUnit进行单元测试
参考:http://www.open-open.com/lib/view/open1328152424546.html
2. Robolectric
http://robolectric.org/getting-started/
在安卓模拟器或者真机上跑测试用例速度很慢。构建、部署、启动app,通常需要花费很久。Robolectric提供一种更好的方式,它模拟了android sdk中的jar包,可以直接在jvm中运行测试用例,这样就大大节省了时间。
另外,使用JUnit对fragment进行单元测试,可以参考该文章:http://blog.denevell.org/android-testing-fragments.html


首先我们需要明确安卓的单元测试主要分为两种类型:

  • 在开发主机Java虚拟机上运行的Junit Test
  • 在Android真机或者虚拟机上运行的Instrumented unit tests

因为Junit Test免去了将apk包安装到android设备上的步骤,因此速度上会比Instrumented unit tests快很多。但是跟android相关的一些库只能运行在android设备上,而无法再本机Java虚拟机上运行,比如很多包名中带android的库。因此我们这里需要做一个选择,如果某些类没有用到android相关的库,则完全可以使用Junit Test,加快测试速率。


我们新创建一个工程后,项目在Android Studio的Project中应该是这样的,其中androidTest包中存放的是Instrumented unit tests的文件,而test中则是Junit Test,这两个文件夹在项目创建的时候AS都会帮我们创建好:

图片 1

QQ截图20170118141231.png

分析一下:
(1)androidTest这个文件夹里的测试类主要是对android的例子进行单元测试。需要运行于设备之上。
(2)test这个文件夹的测试类主要对java的例子进行单元测试,无需运行于设备之上。

在你的gradle中加入Junit的依赖,注意这里的依赖方式是测试期间的依赖(testCompile)

testCompile 'junit:junit:4.12'

单元测试是在你的应用中基本的测试策略。通过创建和运行单元测试检验你的代码,你可以很容易校验个别单元的逻辑是否正确。当你重构代码时,运行单元测试能帮助你快速的修复软件和复原。

编写java测试用例

如果所写的测试代码没有使用android sdk(android.***下的代码),那么可以在test目录下新建,本例中即为ExampleUnitTes

注意测试用例即一个public void的方法,并且加上@Test注解,这是Junit的标准用法

public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() throws Exception {
        assertEquals(4, 2 + 2);
    }
}

编写好测试代码后,有如下两种方法运行

图片 2

QQ截图20170118141829.png

成功的例子

图片 3

QQ截图20170118141733.png

失败的例子

图片 4

QQ截图20170118162359.png

还有一种失败的情况 ,因为用了Android的代码,所以这样子了,这就引出我们的Android测试例子。

图片 5

QQ截图20170118162444.png

单元测试通常是反复测试尽可能小的代码单元(可以是一个方法,类或者组件)。你应该在你的app中的特定代码逻辑需要校验的时候创建单元测试。举个例子来说:如果你正在对一个类进行单元测试,你的测试可能会检查那个类是否处于一个正常的状态。通常被测试的代码单元是孤立的。你的测试仅仅影响和检测那个单元的变化。 mocking framework可以被用来使你的单元从他的依赖上隔离。

编写Android测试用例

/**
 * Created by dell on 2017/1/18.
 *
 *  测试用例类需要使用注解:@MediumTest和@RunWith(AndroidJUnit4.class)
 */
@MediumTest
@RunWith(AndroidJUnit4.class)
public class MTest {
    /**
     *
     * 我们在编写测试函数的时候需要注意两点:

         测试函数需要为public
         测试函数需要添加@Test注解
     */
    @Test
    public void test1() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getTargetContext();
        assertEquals("uuch.com.android_activityanim", appContext.getPackageName());

        Log.i("tag", "$");
        assertEquals("result:", 123, 100 + 33);
    }
}

运行方式和以上 java测试用例一样。

注意:单元测试并不适合测试复杂的UI相互作用的事件。取而代之的是,你应该使用UI 测试框架,在 Automating UI Tests 中有描述。

常见的单元测试框架

为了测试Android apps,你通常会创建这些种类的自动化单元测试:

JUnit4

这是Java界用的最广泛,也是最基础的一个框架 。在Android项目里面使用JUnit是很简单的,你只需要将JUnit这个library加到你的dependencies里面。

testCompile 'junit:junit:4.12'

如果你通过AndroidStudio创建一个项目,这个dependency默认是加上了的,所以你甚至这步都可以省略。

上面java测试用例就是用得这个框架,相对来说是比较简单,也是比较容易理解的 。其中Assert部分,可以帮我们验证一个方法的返回结果。然而,这些只能帮我们测试有返回值的那些方法;没有返回值的方法,即void方法,则要通过另外一个框架,Mockito,来验证它的正确性

  • Local tests:单元测试只在你本地的机器上运行,这些测试在最短时间内被编译在Java虚拟机上运行。通过这个途径运行单元测试不必依赖Android框架或者和使用摸你对象填充有依赖关系。
  • Instrumented tests:在Android设备或模拟器上运行单元测试。这些测试可使用仪器信息,比如为了app测试使用Context上下文。通过这个途径运行单元测试拥有Android的依赖不可以轻易的使用模拟对象。

Mockito

这个是Java界使用最广泛的一个mock框架。

** Building Local Unit Tests **学习如何创建运行在你本地机器上的单元测试

Robolectric3.0

官网地址
Github地址
Robolectric环境搭建(Android Studio + Gradle)
Android 单元测试--Robolectric

Instrumentation与Roboletric都是针对 Android 进行单元测试的框架,前者在执行 case 时候是以 Android JUnit 的方式运行,因此必须在真实的 Android 环境中运行(模拟器或者真机),而后者则是以 Java Junit 的方式运行,这里就脱离了对 Android 环境的依赖,而可以直接将 case 在 JVM 中运行,大赞,因此很适合将 Roboletric 用于 Android 的测试驱动开发。


官网介绍
Running tests on an Android emulator or device is slow! Building, deploying, and launching the app often takes a minute or more. That’s no way to do TDD. There must be a better way.
Wouldn’t it be nice to run your Android tests directly from inside your IDE? Perhaps you’ve tried, and been thwarted by the dreaded java.lang.RuntimeException: Stub!?
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. With Robolectric you can write tests like this:

对于Android app来说,写起单元测试来瞻前顾后,一方面单元测试需要运行在模拟器上或者真机上,麻烦而且缓慢,另一方面,一些依赖Android SDK的对象(如Activity,TextView等)的测试非常头疼,Robolectric可以解决此类问题,它的设计思路便是通过实现一套JVM能运行的Android代码,从而做到脱离Android环境进行测试。

使用
在app/gradle中加入依赖关系

dependencies {
    testCompile "org.robolectric:robolectric:3.2.2"
}

在app/src/test 目录下,创建测试用例 ,运行方式和上面的一样, 第一次会比较慢

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class,sdk = 19)
public class JustTest {

    @Before
    public void setUp() throws URISyntaxException {
        //输出日志
        ShadowLog.stream = System.out;
    }
    @Test
    public void testActivity() {
        MainActivity sampleActivity = Robolectric.setupActivity(MainActivity.class);//创建Activity
        Log.i("","_____________" + sampleActivity.getClass().getSimpleName());
        Assert.assertNotNull(sampleActivity);//判断是否为空
        Assert.assertEquals(sampleActivity.getClass().getSimpleName(), "MainActivity");//获取Activity的标题
    }
}

如果想要支持Fragment

//    如果使用support的Fragment,需添加以下依赖
   testCompile 'org.robolectric:shadows-support-v4:3.1.2'

shadow-support包提供了将Fragment主动添加到Activity中的方法:SupportFragmentTestUtil.startFragment(),简易的测试代码如下

** Building Instrumented Unit Tests **学习如何创建运行在Android设备或者模拟器上的单元测试

Instrumentation

Android Instrumentation简介

Instrumentation框架主要是依靠控件的ID来进行定位的,拥有成熟的用例管理系统,是Android主推的白盒测试框架。若想对项目进行深入的、系统的单元测试,基本上都离不开Instrumentation这把屠龙宝刀。

我在这写的两个小例子都是非常简单的,无法作为应用在项目中的参考,如果有你有这个需求,需要你自己去看这几个测试框架的api文档!

参考文档

Android单元测试框架Robolectric3.0介绍(一)
使用强大的 Mockito 测试框架来测试你的代码
FaceBook 开源的一个测试网络访问速度的库,适用于安卓 APP
Android Espresso 测试框架介绍
在Android Studio中进行单元测试和UI测试
Android单元测试研究与实践
Android developer 测试支持库
要了解android单元测试,首先必须了解junit
Android单元测试框架Robolectric3.0介绍(一)
关于安卓单元测试,你需要知道的一切

首先引入测试框架:

dependencies { testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:1.10.19'}

JUnit是Java里最受欢迎也是应用最广泛的测试框架,而mockito是模拟测试框架。这二者有什么关系呢?我们写代码的时,各种类之间充满了依赖关系。当你测试一个类的时候,可能并不想测试他所依赖的类是否正常,因为你默认它是正常好用的。那么这个时候你就可以用Mockito框架,创建一个模拟对象,JUnit 4比之前好用了不少,只用添加各种注解就能完成简单的测试,看到这你一定很感兴趣了,那么让我们来看一段简单的代码:首先在新建项目,在项目里代码文件夹下新建一个Calculator类:

/** * Created by xiasuhuei321 on 2017/2/4. * author:luo * e-mail:xiasuhuei321@163.com */public class Calculator { public double sum(double a, double b){ return 0; } public double substract(double a, double b){ return 0; } public double divide(double a, double b){ return 0; } public double multiply(double a, double b){ return 0; }}

可以看到我都是返回0,故意的,看看等会测试能不能测出来,接着在test的代码文件夹下新建CalculatorTest测试类:

import org.junit.Before;import org.junit.Test;import static org.junit.Assert.*;/** * Created by xiasuhuei321 on 2017/2/4. * author:luo * e-mail:xiasuhuei321@163.com */public class CalculatorTest { private Calculator mCalculator; @Before public void setUp() throws Exception { this.mCalculator = new Calculator(); } @Test public void sum() throws Exception { assertEquals(6d, mCalculator.sum, 0); } @Test public void substract() throws Exception { assertEquals(1d, mCalculator.substract, 0); } @Test public void divide() throws Exception { assertEquals(4d, mCalculator.divide, 0); } @Test public void multiply() throws Exception { assertEquals(10d, mCalculator.multiply, 0); assertEquals(0d, mCalculator.multiply,0); }}

上面@Before意思是在 @Test注解的方法之前执行这个方法,可以用来初始化一些类。@Test自然就是测试方法了。

接着右键点击测试类,选择Run:

图片 6运行

]结果显而易见没通过测试:

图片 7没通过测试

整个过程都没有将程序运行到Android设备上,和上面讲的一样,这就是在本地的JVM虚拟机上跑的,很方便。现在我们将Calculator类改为正确的逻辑:

/** * Created by xiasuhuei321 on 2017/2/4. * author:luo * e-mail:xiasuhuei321@163.com */public class Calculator { public double sum(double a, double b){ return a + b; } public double substract(double a, double b){ return a - b; } public double divide(double a, double b){ return a / b; } public double multiply(double a, double b){ return a * b; }}

再次运行刚写好的单元测试,看看结果:

图片 8通过

这次通过了,可以发现在修改源代码之后,可以通过这个单元测试的代码来校验修改后的代码逻辑。这对于重构代码来说很有帮助。

上面只是一个简单的例子,在实际代码中,可能我们通过构造方法创建一个对象的时候,还需要依赖另外一个对象,但是如果依赖的对象对我这个测试并没有什么影响,那么就可以用Mockito测试框架来创建一个模拟对象。下面的小例子只是为了说明一下创建模拟对象,就不去耗费脑细胞想应用场景了:

import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.mockito.Mock;import org.mockito.MockitoAnnotations;import org.mockito.runners.MockitoJUnitRunner;import static org.junit.Assert.*;import static org.mockito.Mockito.*;/** * Created by xiasuhuei321 on 2017/2/4. * author:luo * e-mail:xiasuhuei321@163.com */@RunWith(MockitoJUnitRunner.class)public class MockTest { @Mock Context mContext; @Test public void testAppName() { when(mContext.getString(R.string.app_name)) .thenReturn("JpushDemo"); assertEquals("名字不同!", "JpushDemo", mContext.getString(R.string.app_name)); }}

可以看到我类里有一个@Mock注解的Context类引用,在测试方法里我通过这个Context拿到了一个值,然后对比这两个值是否一致。这里就不贴通过测试的图了,各位感兴趣可以自己去看api文档。

原话是Building Instrumented Unit Tests,上面介绍的是在本地JVM虚拟机上运行的检测单元逻辑的测试,这里的可以用来检测UI之类的逻辑,上一个网上看到的例子。

首先是配置环境,我没怎么配置,可能是高版本的as已经自己加入了这个测试框架了,我只在app下的build.gradle里的android下加入了这句话:

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

xml布局:

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:andro xmlns:tools="http://schemas.android.com/tools" android: android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.xiasuhuei321.jpushdemo.MainActivity"> <TextView android: android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="hello" /> <EditText android: android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/textView" android:hint="Enter your name here" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/editText" android:onClick="sayHello" android:text="Say hello!" /></RelativeLayout>

MainActivity:

package com.xiasuhuei321.jpushdemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.EditText;import android.widget.TextView;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void sayHello { TextView textView =  findViewById(R.id.textView); EditText editText =  findViewById(R.id.editText); textView.setText("Hello," + editText.getText().toString; }}

测试代码:

package com.xiasuhuei321.jpushdemo;import android.support.test.filters.LargeTest;import android.support.test.rule.ActivityTestRule;import android.support.test.runner.AndroidJUnit4;import org.junit.Rule;import org.junit.Test;import org.junit.runner.RunWith;import static android.support.test.espresso.Espresso.onView;import static android.support.test.espresso.action.ViewActions.click;import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;import static android.support.test.espresso.action.ViewActions.typeText;import static android.support.test.espresso.assertion.ViewAssertions.matches;import static android.support.test.espresso.matcher.ViewMatchers.withId;import static android.support.test.espresso.matcher.ViewMatchers.withText;/** * Created by xiasuhuei321 on 2017/2/4. * author:luo * e-mail:xiasuhuei321@163.com */@RunWith(AndroidJUnit4.class)@LargeTestpublic class MainActivityInstrumentationTest { private static final String STRING_TO_BE_TYPED = "Peter"; @Rule public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class); @Test public void sayHello() { onView(withId(R.id.editText)) .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard; onView(withText("Say hello!")).perform; String expectedText = "Hello," + STRING_TO_BE_TYPED + "!"; onView(withId(R.id.textView)) .check(matches(withText(expectedText))); }}

看一下运行的效果图:

图片 96.gif

最后放下几个测试框架的api文档地址:JUnit:

Mockito:

Espresso:

这几个例子不是很详细,因为我也是在摸索。。。

本文由华夏彩票发布于编程应用,转载请注明出处:App单元测验

您可能还会对下面的文章感兴趣: