Android自动化测试及性能优化

软件自动化测试对于程序员来说能够确保软件开发的质量和效率。在Android编程中,Android集成了Junit单元测试框架,因此我们可以通过Junit来进行单元测试以及自动化测试。

1、引入Junit测试框架

在Manifest.xml配置文件中,我们需要引入Junit测试框架,因此需要在配置文件中添加如下代码:

在<application>标签中添加Junit测试库:

<uses-library android:name="android.test.runner" />


在<manifest>标签中添加以下代码:

    <instrumentation
        android:name="android.test.InstrumentationTestRunner"
        android:label="Tests for My App"
        android:targetPackage="com.jony.androidtestdemo" />

其中android:name="android.test.InstrumentationTestRunner"表示引入Android测试类

android:label="Tests for My App"该属性的具体内容根据实际情况自行定义

android:targetPackage="com.jony.androidtestdemo"该属性定义了需要测试的包名

至此,进行单元测试需要配置的工作就全部完成了。接下来简单写几个测试用的Activity——模拟实现用户登录过程

2、业务代码编写

MainActivity.java主要作用是实现Activity界面的跳转,通过点击界面上的Button按钮,跳转到用户登录界面;

LoginActivity.java主要作用是接收用户输入的用户名和密码;

HomeActivity.java用户登录成功界面。

以上三个界面的逻辑非常简单,本文重点在于讲解Android自动化测试以及性能优化,因此复杂的业务逻辑在此就不做过多介绍。

3、单元测试

在进行单元测试前,大家可能最关注的是:怎样获得Activity的实例?

的确,这个问题是我们必须要关心的。既然我们能想到的问题,想必Android的大牛们已经给我们提供了怎么获得Activity实例的方法了。
在Android单元测试中,我们只要继承ActivityInstrumentationTestCase2 类,在其构造函数中传入需要测试的Activity,就能通过getActivity()方法获得我们需要测试的Activity的实例,具体代码如下所示:

MainActivity.java的测试代码如下所示:

package com.jony.androidtestdemo.test;

import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import android.view.View;

import com.jony.androidtestdemo.MainActivity;
import com.jony.androidtestdemo.R;

public class MainActivityTest extends
        ActivityInstrumentationTestCase2<MainActivity> {

    private static final String TAG = "MainActivityTest";

    private Instrumentation mInstrument;

    private MainActivity mActivity;

    private View mToLoginView;

    public MainActivityTest() {
        super(MainActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        mInstrument = getInstrumentation();
        // 启动被测试的Activity
        mActivity = getActivity();
        mToLoginView = mActivity.findViewById(R.id.to_login);
    }

    public void testPreConditions() {
        // 在执行测试之前,确保程序的重要对象已被初始化
        assertTrue(mToLoginView != null);
    }

    // mInstrument.runOnMainSync(new Runnable() {

    // public void run() {

    // mToLoginView.requestFocus();

    // mToLoginView.performClick();

    // }

    // });

    @UiThreadTest
    public void testToLogin() {
        // @UiThreadTest注解使整个方法在UI线程上执行,等同于上面注解掉的代码
        mToLoginView.requestFocus();
        mToLoginView.performClick();

    }

    @Suppress
    public void testNotCalled() {
        // 使用了@Suppress注解的方法不会被测试
        Log.i(TAG, "method 'testNotCalled' is called");
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }
}

LoginActivity.java测试代码如下所示:

package com.jony.androidtestdemo.test;

import android.app.Instrumentation;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.EditText;
import com.jony.androidtestdemo.LoginActivity;
import com.jony.androidtestdemo.R;

public class LoginActivityTest extends
        ActivityInstrumentationTestCase2<LoginActivity> {

    private static final String TAG = "LoginActivityTest";
    private Instrumentation mInstrument;

    private LoginActivity mActivity;

    private EditText mUsernameView;

    private EditText mPasswordView;

    private View mSubmitView;

    private View mResetView;

    public LoginActivityTest() {
        super(LoginActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        /*
         * 要向程序发送key事件的话,必须在getActivity之前调用该方法来关闭touch模式
         * 否则key事件会被忽略
         */
        setActivityInitialTouchMode(false);
        mInstrument = getInstrumentation();
        mActivity = getActivity();
        Log.i(TAG, "current activity: " + mActivity.getClass().getName());
        mUsernameView = (EditText) mActivity.findViewById(R.id.username);
        mPasswordView = (EditText) mActivity.findViewById(R.id.password);
        mSubmitView = mActivity.findViewById(R.id.submit);
        mResetView = mActivity.findViewById(R.id.reset);

    }

    public void testPreConditions() {
        assertTrue(mUsernameView != null);
        assertTrue(mPasswordView != null);
        assertTrue(mSubmitView != null);
        assertTrue(mResetView != null);

    }

    public void testInput() {
        input();
        assertEquals("1231", mUsernameView.getText().toString());
        assertEquals("11", mPasswordView.getText().toString());

    }

    public void testSubmit() {
        input();
        mInstrument.runOnMainSync(new Runnable() {
            public void run() {
                mSubmitView.requestFocus();
                mSubmitView.performClick();
            }
        });

    }

    public void testReset() {
        input();
        mInstrument.runOnMainSync(new Runnable() {
            public void run() {
                mResetView.requestFocus();
                mResetView.performClick();
            }
        });
        assertEquals("", mUsernameView.getText().toString());
        assertEquals("", mPasswordView.getText().toString());

    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

    private void input() {
        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                mUsernameView.requestFocus();
            }
        });
       // 因为测试用例运行在单独的线程上,这里最好要
        // 同步application,等待其执行完后再运行
        mInstrument.waitForIdleSync();
        sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2,
        KeyEvent.KEYCODE_3);
        // 效果同上面sendKeys之前的代码
        mInstrument.runOnMainSync(new Runnable() {
            public void run() {
                mPasswordView.requestFocus();
            }
        });
        sendKeys(KeyEvent.KEYCODE_1);
    }

}

HomeActivity.java测试代码如下所示:

package com.jony.androidtestdemo.test;

import android.content.Intent;
import android.test.ActivityUnitTestCase;
import android.widget.TextView;

import com.jony.androidtestdemo.HomeActivity;
import com.jony.androidtestdemo.R;

public class HomeActivityTest extends ActivityUnitTestCase<HomeActivity> {
    private static final String TAG = "HomeActivityTest";
    private static final String LOGIN_CONTENT = "username:1231\npassword:11";
    private HomeActivity mHomeActivity;
    private TextView mLoginContentView;
    public HomeActivityTest() {
        super(HomeActivity.class);
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        Intent intent = new Intent();
        intent.putExtra(HomeActivity.EXTRA_USERNAME, "1231");
        intent.putExtra(HomeActivity.EXTRA_PASSWORD, "11");
        // HomeActivity有extra参数,所以我们需要以intent来启动它
        mHomeActivity = launchActivityWithIntent("com.jony.androidtestdemo",
                HomeActivity.class, intent);
        mLoginContentView = (TextView) mHomeActivity
                .findViewById(R.id.login_content);
    }

    public void testLoginContent() {
        assertEquals(LOGIN_CONTENT, mLoginContentView.getText().toString());
    }

    @Override
    public void tearDown() throws Exception {
        super.tearDown();
    }

}

到此,简单的测试代码就完成了,接下来就是验证代码能否正常运行。

首先,需要将Android应用程序安装到手机或者模拟器上面才能运行测试代码,打开Eclipse的Outline窗口,选中需要测试的方法,单击右键选择Run As-->Android Junit Test

如果出现下图的绿色状态栏则说明运行成功。

Android自动化测试及性能优化_第1张图片

Android性能测试

在Android的SDK的tools 目录中,Android为我们提供了一个性能测试工具traceview,通过traceview功能我们能够确切了解每个方法执行的时间,调用次数以及递归调用测试等信息。通过分析各方法的调用时间可以了解系统的瓶颈,提升系统性能。

使用方法:

在需要进行性能分析的Activity中加入以下代码(一般在Activity的onCreate()方法中添加):

Debug.startMethodTracing("jonytest");//jonytest.trace

在onDestroy()方法中添加:

Debug.stopMethodTracing();// 停止数据采集

运行程序,当程序运行到该Activity是,系统会自动记录该Activity的所调用的所有方法的信息,并生成jonytest.trace文件保存在SDcard中。将该文件导出到电脑C盘,运行以下命令:traceview jonytest.trace 系统将会弹出如图所示窗口(可以在Find窗口查找具体方法(目前只支持小写)):

Android自动化测试及性能优化_第2张图片

至此,Android单元测试和系统优化已经给大家介绍完了,知识融会贯通,希望对大家有所帮助。



你可能感兴趣的:(Android自动化测试及性能优化)