[AndroidUITestRunner]面向UI的单元测试框架

这是以前在团队中写的一个面向Android应用的测试工具,在团队中使用效果不错,现在整理出来在这里分享,希望能够帮到有需要的人。

AndroidUITestRunner 可以有什么用处?

通常我们使用的单元测试框架都是用来测试一些非UI的逻辑的,如JUnit,CPPUnit。我希望团队中的成员都要养成写单元测试的习惯,一方面单元测试本身就是一份非常好的文档,另一方面单元测试有助于强迫使你的代码耦合更加松散(模块可以独立测试), 在移动应用开发中我遇到一些这样的问题。

你知道移动应用一般都是MVC的结构,Mode层面使用JUnit进行单元测试很方便,但是想测试View就无能为力了,或者说View基本上就代码中直接实现了。在实际开发的过程中往往产品经理或者设计师会要求程序员实现一种特定的效果,那么我们通常的做法就是创建一大堆Demo工程来演示View效果很不方便,而且和项目本身也没有什么实际的联系回头还是要拷贝代码到实际项目中。所以为了更加方便测试项目中的一些界面效果,我便实现了“AndroidUITestRunner”这么一个工具。你可以把每一个布局或者界面效果写成测试用例和项目一起构建,然后安装在你的设备上点击运行你的测试用例,让设计师或产品经理看看你的UI实现是否OK,最重要的是团队成员可以很快达到并行工作状态,一部分人写传统的测试用例,另外一部分人可以立刻实现设计师要求UI用例,一旦UI测试用例和传统的测试用例达到预期,接下来只需要实现Controller部分把Model和View集成进来就好了。我自己在开发xbrowser 的时候也是使用这个框架,很多需要验证想法的逻辑都放在了测试用例里面,事实证明无论是开发效率还是代码稳定性都有很大提升。

AndroidUITestRunner提供了类似JUnit的功能,你只要写一个以"test"开头的方法框架就会自动的把这些以"test"开头的用例组织起来生成一个列表,点击列表项就可以看到UI的演示效果,如下图所示。

测试套件列表

这是一个测试套件的列表,每个列表包括一系列相关的测试用例。

测试用例列表

打开测试套件你将会看到某个测试套件下的测试用例列表,点击某个测试用例就好看到响应的执行效果。下面介绍如何正好AndroidUITestRunner到你的项目中。

在你的项目中使用UITestRunner

Step 1 从github上下载UITestRunner源码

git clone https://github.com/examplecode/android-ui-test-runner

Step 2 导入AndroidUITestRunner 到 你的项目中

下面分别介绍了,如何导入AndroidUITestRunner到你的AndroidStdio工程和Eclipse工程中。

导入到AndroidStdio

1. 选择 File-> New -> Import Module 导入模块
选择导入模块
选择UiTestRunner模块所在位置
导入模块后项目视图结构
2. 设置项目依赖 uiTestRunner 模块
打开模块设置选项
添加模块依赖
选择uiTestRunner模块

导入UITestRunner 到 Eclipse

1. 导入UiTestRunner
选择import
选择导入已有项目
选择AndroidUITestRunner
导入后的项目结构视图
2. 设置项目依赖UiTestRunner模块
选择项目属性
添加UiTestRunner为lib依赖
添加后的结果

Step 3 在你的项目中添加UITestRunner的声明

在你自己项目的AndroidManifest.xml中添加以下内容声明UITestActivity(测试用例的展示界面)

<activity android:name="com.mmbox.uitestrunner.UITestActivity">
    <intent-filter>
        <action android:name="com.mmbox.uitestrunner.Main" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Step 4 创建你自己的测试套件

你可以根据不同的测试类型创建多个测试套件,创建测试套件很简单只需要在你的项目中新建一个Class从UITestSuite继承即可(框架会自动搜索从UITestSuite继承的所有测试套件并在列表中呈现出来)。建议我们创建一个package来存放所有的测试套件(不是必须只是为了清楚易于维护,包名可以自己定义) 比如 "com.myapp.testsuites", 下面是一个测试套件的代码实现。

public class MySampleTestSuite1 extends UITestSuite {
    
    public MySampleTestSuite1(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
}

Step5 为已有的测试套件添加测试用例

添加测试用例也很简单,只需要在测试套件中增加一些以 "test" 开头的 public 方法即可,如以下代码:

public class MySampleTestSuite1 extends UITestSuite {
    public MySampleTestSuite1(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
    }
    
    public void testSayHello() {
        Toast.makeText(getContext(), "Hello UITestRunner",Toast.LENGTH_SHORT).show();
    }
    
    public void testCase1InTestSuite1() {
        Toast.makeText(getContext(), "testCase1InTestSuite1 is runngin",Toast.LENGTH_SHORT).show();
    }
    
    public void testCase2InTestSuite1() 
    {
        Toast.makeText(getContext(), "testCase2InTestSuite1 is runngin",Toast.LENGTH_SHORT).show();
    }
    
}

Step6 启动测试用例

到此我们就完成了一个测试套件及相关的测试用例了,接下来我们看看如何运行测试用例。

运行测试套件要借助于adb命令,首先安装你的应用到目标设备或模拟器上,并确保你的设备和adb正常连接( adb devices 命令可以发现你的设备) 。接下来只需要执行下面的命令就可以在应用界面中展现你的测试用例。

adb shell am start -a com.mmbox.uitestrunner.Main

一切正常的话你将会看到类似下面的界面,在这里我实现了三个测试套件.

Paste_Image.png

一些简单的示例

我们除了在测试用例测试一些简单的逻辑外,还有一个经常需要的场景就是想看看一个布局文件的实际显示效果,这时候我们没有必要创建一个新的Activity来显示你的View,只需要在测试用例中调用showTestView方法即可. 框架提供了两个方法 showView,showLayout,分别用于显示一个View实例和展示一个布局文件。

显示一个视图

下面的代码新增一个测试用例,用来显示一个Button。


public void testShowSimpleButton() {
    Button btn = new Button(getContext());
    btn.setText("Click Me");
    btn.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
    showView(btn);
}
Paste_Image.png

显示一个布局文件

下面的代码用于展示一个布局文件的显示效果.

    public void testShowSimpleLayout() {
        showLayout(R.layout.show_layout_demo);
    }

布局文件的源码如下:

<!-- Demonstrates using nextLeft, nextRight, nextUp and nextDown to get
     focus behavior that would be difficult with default focus calculation alg.-->

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="10dip">

    <Button android:id="@+id/top"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:nextFocusDown="@+id/bottom"
        android:text="Top"/>

    <Button android:id="@+id/right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:nextFocusLeft="@+id/left"
        android:text="Right"/>

    <Button android:id="@+id/bottom"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:nextFocusUp="@+id/top"
        android:text="Bottom"/>

    <Button android:id="@+id/left"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"
        android:nextFocusRight="@+id/right"
        android:text="Left"/>

</RelativeLayout>

展示效果

Paste_Image.png

如果觉得这个东东有用,不妨在github Start 一下搜藏起来, :) 。

推荐阅读更多精彩内容