Android单元测试介绍

jopen 8年前

 Android的测试支持库为测试Android应用提供了大量框架。该库提供了一组API快速构建和运行测试代码,包括JUnit4和功能用户界面(UI)测试。可以从Android Studio IDE中或命令行这执行。

Android的测试支持库可通过Android SDK管理器获取。

测试支持库特性

AndroidJUnitRunner:兼容JUnit 4测试运行器。 Espresso:UI测试框架;适合在单个应用的功能UI测试。 UI Automator:UI测试框架;适用于跨应用的功能UI测试及安装应用。

AndroidJUnitRunner

AndroidJUnitRunner类是JUnit测试运行器,可以让你在Android设备上执行JUnit3或JUnit4中风格的测试类,兼容Espresso和UI Automator测试框架。测试运行器加载测试包和应用,运行测试并报告测试结果。该类取代 InstrumentationTestRunner类(仅支持JUnit 3)。

这个运行器的主要特点:

  • JUnit支持

  • 获得Instrumentation信息

  • 测试筛选

  • 测试分片

要求的Android2.2(API 8)或更高。

 

测试运行器兼容JUnit3和JUnit4的(最高JUnit4.10)测试。在同一个包混淆JUnit 3和和JUnit4测试代码可能会导致不可预测的结果。instrumented JUnit 4测试类在设备或仿真器上运行,必须在前面加上@RunWith(AndroidJUnit4.class)注释。

 

比如测试CalculatorActivity类中的加操作:

 

import android.support.test.runner.AndroidJUnit4;  import android.support.test.runner.AndroidJUnitRunner;  import android.test.ActivityInstrumentationTestCase2;    @RunWith(AndroidJUnit4.class)  public class CalculatorInstrumentationTest          extends ActivityInstrumentationTestCase2<CalculatorActivity> {                @Before      public void setUp() throws Exception {          super.setUp();                    // Injecting the Instrumentation instance is required          // for your test to run with AndroidJUnitRunner.          injectInstrumentation(InstrumentationRegistry.getInstrumentation());          mActivity = getActivity();      }            @Test      public void typeOperandsAndPerformAddOperation() {          // Call the CalculatorActivity add() method and pass in some operand values, then          // check that the expected value is returned.      }            @After      public void tearDown() throws Exception {          super.tearDown();      }  }

InstrumentationRegistry类可以访问测试运行的信息。该类包括Instrumentation对象,目标应用上下文对象,测试应用上下文对象及传递到测试的命令行参数。

JUnit 4.x的测试可以使用annotation来配置测试运行,并支持Androidannotation:

@RequiresDevice:物理设备上运行。

@SdkSupress:限定最低SDK版本。例如@SDKSupress(minSdkVersion=18)
@SmallTest,@MediumTest和@LargeTest:测试分级。

单个test suite可以分片,同一Instrumentation的同一分片可以作为一个组。每个片都有索引号。当运行测试,使用-e numShards选项指定片数和-e shardIndex选项来指定要运行的片。

例如分成10个碎片,仅执行第二片测试,请使用以下命令:

adb shell am instrument -w -e numShards 10 -e shardIndex 2

Espresso

Espresso提供了一组API来构建UI测试来测试用户流程。这些API让你写简洁和可靠运行的自动化UI测试。Espresso非常适合白盒自动测试,测试代码利用了被测应用的代码细节。

Espresso的主要特性:

  • 视图匹配(View matching): 灵活的API用于查看和适配目标应用。

  • Action API:一套扩展的action API自动化UI交互。

  • UI线程同步(UI thread synchronization)以提高测试的可靠性。

要求Android2.2(API 8)或更高。

Espresso.onView()方法可以访问目标应用程序的UI组件并与之交互。该方法接受一个Matcher参数,搜索视图层来定位视图实例。定位方法可以基于类名、内容描述、R.id、显示的文本。比如

onView(withId(R.id.my_button));


如果搜索成功,onView()方法返回对应view的引用,可执行用户操作和断言。

AdapterView由子view动态生成。如果目标视图在AdapterView(ListView或GridView)中,onView()方法可能不起作用,因为可能只加载了一部分,Espresso.onData()则可以。

ViewActions可以执行视图点击(View clicks),滑动(Swipes),按键或者按钮(Key and button press)、文本输入(Typing text)、打开链接(Opening a link)。

// Type text into an EditText view, then close the soft keyboard  onView(withId(R.id.editTextUserInput))      .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());  // Press the button to submit the text change  onView(withId(R.id.changeTextBt)).perform(click());

由于时间问题,在Android设备上测试随机失败。之前一般通过sleep和超时处理解决。Espresso测试框架处理Instrumentation和UI线程之间的同步,很好地解决了这些问题。

API参考:developer.android.com/reference/android/support/test/package-summary.html
测试参考:http://developer.android.com/training/testing/ui-testing/espresso-testing.html

 

UI Automator

UI Automator提供了一组API来构建基于交互UI的测试。API允许你执行操作,如打开设置菜单,非常适合黑盒自动化测试,测试代码不依赖于应用的内部实现。

主要特性:

  • UI Automator Viewer:检查的布局层次。

  • API来获取设备状态信息并执行操作。

  • API跨应用测试。

要求Android4.3(API等级18)或者更高。

uiautomatorviewer提供了一个方便的图形用户界面进行扫描和分析在Android设备上当前显示的UI组件。您可以使用此工具来检查的布局层次和查看UI组件。

UiDevice 类可以访问设备并进行操作。你可以调用它的方法来访问设备属性,如当前的方向或显示尺寸。该UiDevice类也让您执行操作,例如:旋转设备;按下D- pad按钮;按Back、Home、Menu等;打开通知树栏;当前窗口截图等。比如按Home键:UiDevice.pressHome()。

更 多应用相关的API: UiCollection枚举容器的UI元素以计数,或通过文字(或属性等)定位子元素; UIObject表示是在设备上可见的UI元素; UiScrollable:为可滚动UI容器提供查找支持; UiSelector:查询一个或者多个UI元素; Configurator: 设置参数。比如:

// Initialize UiDevice instance  mDevice = UiDevice.getInstance(getInstrumentation());  // Perform a short press on the HOME button  mDevice().pressHome();  // Bring up the default launcher by searching for  // a UI component that matches the content-description for the launcher button  UiObject allAppsButton = mDevice          .findObject(new UiSelector().description("Apps"));  // Perform a click on the button to bring up the launcher  allAppsButton.clickAndWaitForNewWindow();

API参考:http://developer.android.com/reference/android/support/test/package-summary.html
实例:http://developer.android.com/training/testing/ui-testing/uiautomator-testing.html

https://pypi.python.org/pypi/uiautomator 用python封装了uiautomator,操作起来更简单,推荐使用。

 

测试支持库安装

android {      defaultConfig {          testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"      }  }

 

  • 启动Android SDK管理器。

  • 在SDK管理器窗口,滚动到软件包列表的末尾,找到其他文件夹,如有必要,展开以显示其内容。

  • 选择 Android Support Repository。

  • 点击Install packages。

    下载后安装支持库文件到您现有的Android SDK目录。该库文件位于你的SDK的子目录:<sdk>/extras/android/m2repository。

    Android的测试支持库位于android.support.test包。

    Gradle中使用:build.gradle文件中添加这些依赖关系:


    dependencies {    androidTestCompile 'com.android.support.test:runner:0.4'    // Set this dependency to use JUnit 4 rules    androidTestCompile 'com.android.support.test:rules:0.4'    // Set this dependency to build and run Espresso tests    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.1'    // Set this dependency to build and run UI Automator tests    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'  }



    设置AndroidJUnitRunner为默认测试:
    强烈建议您一起使用Android测试支持库与Android Studio IDE。 Android的Studio支持测试开发,如:

        基于Gradle的灵活的构建系统,支持测试代码的依赖管理。
        单个项目包含应用程序源代码和测试代码。
        支持虚拟或物理设备的部署和运行测试,支持命令行或图形用户界面

    更多介绍参见:http://developer.android.com/sdk/index.html

参考资料

  • Testing Support Library

  • http://wiki.jikexueyuan.com/project/android-weekly/issue-145/android-ui-auto-testing.html

  • http://developer.android.com/intl/zh-cn/training/testing/ui-testing/espresso-testing.html

  • http://www.stevenmarkford.com/android-ui-testing-with-espresso-basics-tutorial/

  • http://www.csdn.net/article/2015-08-19/2825493-using-espresso-for-easy-ui-testing

 

作者博客:http://my.oschina.net/u/1433482/ python测试开发精华群 291184506 PythonJava单元白盒测试 144081101

Android测试基础

android测试框架(Android Testing Framework)是开发环境的一部分,它提供了架构和强大的工具帮助你从单元到框架测试应用的各个方面。

本部分有点偏旧,如果和其他部分内容有冲突,以其他内容为准。


关键特性:

  • 基 于 JUnit,可直接使用JUnit测试一些与Android AP不相关的类,或使用 Android的JUint 扩展来测试 Android 组件。如果你刚开始接触 Android 测试,可以先从 AndroidTestCase开始写一些通用目的的测试用例,然后再写较复杂的测试用例。

  • Android JUint扩展提供了对 Android 特定组件的测试类,提供了创建mock对象辅助方法及控制组件生命周期的方法。

  • Test suite包含在测试包中,操作和主程序包类似。

  • Eclipse ADT包含了创建和测试的SDK 工具,并可通过命令行在其它IDE使用。这些工具从被测试的应用读取信息并自动创建测试包的build 文件,mainfest文件和文件目录结构。

  • SDK 提供了moneyrunner(用python书写脚本)及Monkey(发送伪随机事件的UI压力测试工具)。


本文档描述了android测试框架的基础,包含测试结构、开发测试的API以及启动测试和查看测试结果的工具。本文假设你有android应用编程以及JUnit测试的基础。

测试框架图如下:

测试结构

android的构建和测试工具假设测试项目都组织成类似测试、测试类、测试包和测试项目的标准结构。

android测试基于JUnit。通常JUnit测试就是测试“待测试应用”的方法。测试方法构成的类为test cases (或者test suites)。

JUnit 中编译测试源文件到class文件中。类似地android中用android的编译工具编译测试包中的测试源文件为class文件。在JUnit 中test runner来执行测试类。在android中使用测试工具加载测试包和被测应用,然后调用android的test runner。

 

测试项目

测试项目就是一个目录或者eclipse 工程,可在其中新建源代码、manifest文件以及测试包的其它文件。建议使用Android tool创建测试项目:

  • 自动配置InstrumentationTestRunner为测试执行器。必须使用InstrumentationTestRunner或者其子类来执行JUnit测试。

  • 为测试包取合适的名字。如果被测应用为com.mydomain.myapp,Android tool自动命名测试包名为com.mydomain.myapp.test。

  • 自动创建合适的构建文件、manifest 文件以及目录结构。


推荐的目录结构:


  MyProject/        AndroidManifest.xml        res/            ... (resources for main application)        src/            ... (source code for main application) ...        tests/            AndroidManifest.xml            res/                ... (resources for tests)            src/                ... (source code for tests)

注:这个在实际操作中往往根据IDE而不同,请以具体IDE的实例为准。 


测试API

Android测试API基于JUnit API 并扩展了instrumentation框架和Android特有的测试类。

JUnit


使用JUnit的TestCase类可对未使用android API的类进行单元测试。TestCase也是AndroidTestCase(测试依赖于android的对象)的基类。 AndroidTestCase还提供了android特有的 setup、teardown以及其它帮助方法。

JUnit的Assert类可以显示结果,assert方法比较期望值与实际结果,在比较失败时抛出异常。android同样提供了扩展了比较类型的断言类,以及用来测试UI的断言类。

注意android测试API支持JUnit 3的代码风格,不支持JUnit 4。

Instrumentation

android instrumentation是android系统中一系列控制方法或钩子(hooks)。这些钩子可以的正常生命周期内独立地控制组件。它同样可以控制android如何加载应用。

通常android组件会按照系统指定的生命周期来运行,例如Activity的生命周期开始于它被Intent激活,其onCreate()方法会被调 用,接下来是onResume(),当用户启动另外一个应用,onPause()方法会调用 ,如果Activity调用finish()方法,它的onDestroy()方法也会被调用。android framework API不会提供方法让你在代码中直接调用这些回调方法,但是用instrumentation可以。

系统把应用中的所有组件运行在同一个进程中,你可以让一些组件,比如content provider,运行在单独的进程中。但是无法强制应用与另外其他正在运行的应用运行在同一个进程中。

通过android imstrumentation,你可以在测试代码中直接调用回调方法,让你渗透组件的生命周期,就像调试。下面的测试代码演示了如何用instrumentation来测试Activity保存和恢复状态:
  

    // Start the main activity of the application under test      mActivity = getActivity();            // Get a handle to the Activity object's main UI widget, a Spinner      mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);            // Set the Spinner to a known position      mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);            // Stop the activity - The onDestroy() method should save the state of the Spinner      mActivity.finish();            // Re-start the Activity - the onResume() method should restore the state of the Spinner      mActivity = getActivity();            // Get the Spinner's current position      int currentPosition = mActivity.getSpinnerPosition();            // Assert that the current position is the same as the starting position      assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);

代码中使用的关键方法是getActivity(),它属于android instrumentation API。调用该方法才会启动Activity。你可以提前配置测试所需的环境(test fixture)。

Instrumentation可以加载测试包和被测试应用到同一个进程,这样测试代码可以调用应用组件的方法,修改和检查组件中的域。

Test case 类


android提供了几个继承自TestCase和Assert的test case 类,它们都有andorid 特有的setup、teardown以及其它的辅助方法。


AndroidTestCase


通用的test case类,继承了TestCase和Assert类。它提供了标准的JUnit中的setup()和teardown()方法,同时还有JUnit的Assert方法。另外它也提供了用来测试权限的方法以及通过清除一定的类引用来防止内存泄露的方法。

组件特有的test case

android测试框架的一个重要特点是组件test case类。它们有独特的setup和teardown及控制组件生命周期。同时它们也提供mock方法。

1、Activity Testing

2、Content Provider Testing

3、Service Testing

Android并没有为 BroadcastReceiver提供单独的test case 类。可以通过测试发送Intent对象给它的组件来测试BroadcastReceiver,检查BroadcastReceiver回复是否正确。

ApplicationTestCase


用 ApplicationTestCase测试Application对象的setup和teardown。这些对象维护着应用程序包中所有组件信息的全局 状态,该test case 用于验证manifest 文件中的<application>元素是否正确配置。然而记住这个test case无法控制应用包组件的测试。

InstrumentationTestCase

如果想在test case 类中使用 instrumentation 的方法,必须使用InstrumentationTestCase或者它的子类。Activity test case 继承该基类。

Assertion 类

Android test case 类继承自JUnit,可以用断言来显示测试结果。assertion方法将测试返回的真实值和期望值进行比较,如果比较失败它会抛出AssertionException。用Assertion比打印log更方便,有更好的测试性能。

除了JUnit的Assert类的方法,测试API同时也提供了MoreAsserts和ViewAsserts类:

  • MoreAsserts包含更多强大的断言,例如进行正则表达式匹配的assertContainsRegex(String, String)。

  • ViewAsserts包含很多关于View的断言,例如它包含用来测试View是否在屏幕的特定的(x, y)位置的assertHasScreenCoordinates(View, View, int, int)方法,这些断言简化了UI中的几何和对准测试。

模拟对象类


为了解决测试过程中的依赖,Android提供了创建模拟系统对象的类,比如Context对象、 ContentProvider对象、ContentResolver对象以及Service对象。有些test case能模拟的Intent对象。通过使用这些模拟对象,你可以将测试与系统的其余部分隔离开,同时也满足了测试中的依赖,这些类都在包 android.test和android.test.mock中。

模拟对象通过打桩或者重载正常操作来实现将测试与正在运行的系统隔 离。例如MockContentResolver对象用它自有的与系统隔离的本地框架来代替通常的resolver框架。同时 MockContentResolver不使用notifyChange(Uri, ContentObserver, boolean)方法,这样测试环境以外的observer对象不会被意外触发。

模拟对象类也通过提供正常类的子类来满足测试的依赖,该子类除了你覆写的方法外其它都是不起作用的。例如,MockResources对象是Resources类的子类,每个方法在调用时都会抛出异常。要使用它,你只需要重载需要的方法。

下面是Android中可用的模拟对象类:

基本的模拟对象类

MockApplication、 MockContext、MockContentProvider、MockCursor,、MockDialogInterface、 MockPackageManager和MockResources提供了简单有用的模拟策略(打桩),在调用时都会抛出 UnsupportedOperationException异常。使用它,你只需要重载需要的方法。

注意:MockContentProvider和MockCursor是API Level 8 中新加入。

Resolver 模拟对象

MockContentResolver 通过屏蔽系统正常的resolver框架来为content provider 提供隔离的测试。MockContentResolver不是在系统中查找提供authority的content provider,而是使用自己的内部表,你必须显式地用addProvider(String, ContentProvider)方法将provider添加到表中。

通过这个特性可以将模拟的content provider与authority关联,新建provider对象但使用测试数据,你甚至可以设置provider的authority为null。 实际上MockContentResolver对象将你的测试与包含真实数据的provider隔离。你可以控制provider的功能并防止测试影响真 实数据。


Context测试


android提供了两个Context类来提供测试:

  • IsolatedContext类提供隔离的Context,使用该Context的文件、目录和数据库操作都会在测试区域。尽管功能有限,但足以应对系统调用,允许在不影响当前设备上的真实数据的前提下测试应用的数据操作。

  • RenamingDelegatingContext 的Context的大部分功能都由现有的Context来处理,但是文件和数据库操作都由IsolatedContext来处理,隔离的部分使用一个测试 目录,并且创建特殊的文件和目录名,你可以自己控制命名,也可以让constructor自动指定。该类为进行数据操作创建隔离区域提供了快捷办法,同时 不会影响Context其它的正常操作。

运行测试

test case由test runner类运行,test runner加载test case类、初始化、运行及清理测试。android test runner必须配置,这样启动应用的系统工具可以控制测试包如何加载test case和被测试应用。一般在manifest文件中设定。

InstrumentationTestRunner是android中主要的test runner类,它扩展了JUnit test runner框架并且是已配置,能够执行任何android系统提供的test case 类并且支持所有类型的测试。

你 可指定测试包的manifest文件的<instrumentation>标签内容为Instrumentation 或其子类。InstrumentationTestRunner的代码在共享库android.test.runner中,所以它通常没有链接到你的代 码,必须在<uses-library>标签中指定。通常不需要手动去设定,Eclipse ADT以及android 命令行工具都会自动生成它们并且把它们加到测试包的manifest文件中。

注意:如果使用的是InstrumentationTestRunner之外的test runner,必须修改<instrumentation>标签并指向你想使用的类。

要运行InstrumentationTestRunner类必须用android 工具调用内部系统类。Eclipse ADT中这些类都会被自动调用,命令行工具执行测试的时用Android Debug Bridge (adb)运行这些类。

系 统类加载和启动测试包,杀掉被测试应用包正在运行的进程,并且重新加载被测试包的实体,然后它们把控制权交给 InstrumentationTestRunner,由它来执行测试包中的每个test case。你也可以通过Eclipse ADT中的setting或者命令行工具中的flag来控制哪些 test case或者方法的运行。

既不是系统类也不是 InstrumentationTestRunner运行被测试应用,而是test case。它要么调用被测试包中的方法,要么调用它自己的方法以改变被测试包生命周期。应用程序完全由test case 控制,测试开始前由test case来初始化测试环境,

关于更多的运行测试,可以参见 Testing from Eclipse with ADT Testing from Other IDEs

查看测试结果

Android 测试框架返回测试结果给启动测试的工具。在eclipse ADT中结果会在新的JUnit视图面板中显示,命令行会在STDOUT中显示。两者都可以看到显示每个test case 名字和你所运行的方法的小结,同时会看到所有失败的断言,其中包含指向产生失败的测试代码所在行的链接。失败的断言同时也会列出期望值和实际值。

测试结果根据IDE不同而有不同。


monkey 和 monkeyrunner


SDK提供了两个应用测试工具:

  • UI/Application Exerciser Monkey,通常称为"monkey",向设备发送伪随机事件流(如击键、触控、手势)的命令行工具。你可以通过Android Debug Bridge (adb)来运行它,对应用进行压力测试然后报告遇到的错误。你可以通过每次使用相同的随机数种子来重复步骤。

  • monkeyrunner是一套API,基于Python。该API包含如下功能:连接到设备、安装和卸载软件包、截图、比较图片、运行应用对应的测试应用。通过该API,你可以写出功能强大复杂的测试,通过命令行工具monkeyrunner来运行。

 

处理包名

 

测试环境需要同时处理android应用包名和java包标识符。它们都使用同样的命名格式,但是代表着完全不同的实体。

android 包名是.apk文件对应的一个独一无二的系统名字,由应用包的manifest文件中<manifest>标签中 的"android:package"属性来设定。测试包的名字必须和被测试包的名字不同,通常android工具会用被测试包的名字后加 上".test"来作为测试包的名字。

测试包也会用包名来定位它所测试的应用,元由测试包的manifest文件中<instrumentation>素的"android:targetPackage"属性设定。


java包标识符对应源文件,包名反映了源文件所在目录,它同时会影响类与成员间彼此的可访问性。

android 工具会帮助你设定测试包的名字。根据你的输入,工具会设定测试包的名字以及测试的目标包的名字。只有在被测试应用工程已经存在的情况下这些工具才会起作用。

默 认情况下,这些工具会将测试类的包标识符设定为与被测试应用的包标示符一致。如果你想暴露被测试包中的一些成员你可能需要做一些修改。如果要修改,只修改 java 包标示符,不要修改android 包名,只修改test case 的源文件而不要修改测试包中R.java文件的包名,因为修改它会造成与被测试包中的R.java类冲突。不要将测试包的android包名修改成和它所 测试的应用的包名一样,因为这样它们的名字在系统中不再是独一无二的。

测试内容

What To Test详细地描述了android应用中应该被测试的关键功能以及可能会影响该功能的状况。

大部分的单元测试是专门针对你正在测试的andorid组件。Activity Testing、 Content Provider Testing和 Service Testing中都有一章节列出“需要测试什么”。

尽量在真实的设备上运行这些测试。其次用Android Emulator来加载已经配置好你所希望测试的硬件、屏幕、版本的android vitual device。

 

参考资料

http://developer.android.com/intl/zh-cn/tools/testing/testing_android.html

http://www.uml.org.cn/mobiledev/201306074.asp

本文地址:http://www.cnblogs.com/pythontesting/p/4916574.html



来自: http://my.oschina.net/u/1433482/blog/602003