android基础知识12:android自动化测试03—基于junit的android测试框架02

        本文接上文介绍基于junit的android测试框架。

5、AndroidTestRunner

        随着学习的深入,发现包在前面的篇幅中,我们忽略了android.test包中一个重要的类AndroidTestRunner,这个类是android.test包的核心类,下面为大家详细说明,并补充说明一些相关的内容。

junit.framework包中的TestListener接口
这个接口的函数,列举如下:

package junit.framework;

/**
 * A Listener for test progress
 */
public interface TestListener {
	/**
 	 * An error occurred.
 	 */
	public void addError(Test test, Throwable t);
	/**
 	 * A failure occurred.
 	 */
 	public void addFailure(Test test, AssertionFailedError t);  
	/**
	 * A test ended.
	 */
 	public void endTest(Test test); 
	/**
	 * A test started.
	 */
	public void startTest(Test test);
}
与这个接口,相关的类就只用TestResult,相关接口如下:

	/**
	 * Registers a TestListener
	 */
	public synchronized void addListener(TestListener listener) {
		fListeners.addElement(listener);
	}
	/**
	 * Unregisters a TestListener
	 */
	public synchronized void removeListener(TestListener listener) {
		fListeners.removeElement(listener);
	}
看到这里就应该知道如何使用了,具体的使用在下一篇幅例子中说明。
junit.runner包,结构如下:



这是一个对junit.framework的辅助包,包主要就是BaseTestRunner类,其实现了TestListener接口,主要功能是:对测试过程中Error、Failure的检查。
有了这些补充说明,下面学习android.test包中一个重要的类AndroidTestRunner。
AndroidTestRunner类结构,如下图所示:


其主要接口函数,列举如下:


看到setContext(Context context)这个函数的这个参数Context context,总算让我看到junit与Android的结合点了,在看下其他几个函数,我们会发现,这个类是android.test的核心控制类,大家心中的疑惑顿时就没有了。列举一个简要的例子,如下:

AndroidTestRunner testRunner = new AndroidTestRunner();
testRunner.setTest( new ExampleSuite() );
testRunner.addTestListener( this );
testRunner.setContext( parentActivity );
testRunner.runTest();
通过AndroidTestRunner控制整个测试,并与我们的Activity向结合

6、androidTest例子分析

         前面我们学习了android.test包中的大部分类,是该通过学习具体的例子将前面的知识融会贯通,让我们的理解更加深刻,例子程序代码下载地址,下载后添加Eclipes的工程中,边看这篇文章边阅读例子程序的代码。

首先分析整个工程的结构图,如下:


        AndroidTestCase,Testsuite在前面的篇幅中已经学习过了,ContestTest、MathTest、SomeTest、ExampleSuite在前面的例子中已经为大家介绍了,这里我们主要说明整个程序是如何运行的?

android界面程序如下:

public class JUnit extends Activity {
    static final String LOG_TAG = "junit";
    Thread testRunnerThread = null;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Button launcherButton = (Button)findViewById( R.id.launch_button );
        launcherButton.setOnClickListener( new View.OnClickListener() {
            public void onClick( View view ) {
                startTest();
            }
        } );
    }

    private synchronized void startTest() 
    {
        if( ( testRunnerThread != null ) &&
            !testRunnerThread.isAlive() )
            testRunnerThread = null;
        if( testRunnerThread == null ) {
            testRunnerThread = new Thread( new TestRunner( this ) );
            testRunnerThread.start();
        } else
            Toast.makeText(
                        this, 
                        "Test is still running", 
                        Toast.LENGTH_SHORT).show();
    }

}

class TestDisplay implements Runnable 
{
        public enum displayEvent{START_TEST,END_TEST,ERROR,FAILURE,}
        displayEvent ev;
        String testName;
        int testCounter;
        int errorCounter;
        int failureCounter;
        TextView statusText;
        TextView testCounterText;
        TextView errorCounterText;
        TextView failureCounterText;

        public TestDisplay( displayEvent ev,
                        String testName, 
                        int testCounter,
                        int errorCounter,
                        int failureCounter,
                        TextView statusText,
                        TextView testCounterText,
                        TextView errorCounterText,
                        TextView failureCounterText ) 
        {
            this.ev = ev;
            this.testName = testName;
            this.testCounter = testCounter;
            this.errorCounter = errorCounter;
            this.failureCounter = failureCounter;
            this.statusText = statusText;
            this.testCounterText = testCounterText;
            this.errorCounterText = errorCounterText;
            this.failureCounterText = failureCounterText;
        }

        public void run() 
        {
            StringBuffer status = new StringBuffer();
            switch( ev ) {
                case START_TEST:
                    status.append( "Starting" );
                    break;

                case END_TEST:
                    status.append( "Ending" );
                    break;

                case ERROR:
                    status.append( "Error: " );
                    break;

                case FAILURE:
                    status.append( "Failure: " );
                    break;
                
            }
            status.append( ": " );
            status.append( testName );
            statusText.setText( new String( status ) );
            testCounterText.setText( "Tests: "+testCounter );
            errorCounterText.setText( "Errors: "+errorCounter );
            failureCounterText.setText( "Failure: "+failureCounter );
        }
}

class TestRunner implements Runnable,TestListener  
{
        static final String LOG_TAG = "TestRunner";
        int testCounter;
        int errorCounter;
        int failureCounter;
        TextView statusText;
        TextView testCounterText;
        TextView errorCounterText;
        TextView failureCounterText;
        Activity parentActivity;

        public TestRunner( Activity parentActivity ) 
        {
            this.parentActivity = parentActivity;
        }

        public void run() 
        {
            testCounter = 0;
            errorCounter = 0;
            failureCounter = 0;
            statusText = (TextView)parentActivity.
                                    findViewById( R.id.status );
            testCounterText = (TextView)parentActivity.
                                    findViewById( R.id.testCounter );
            errorCounterText = (TextView)parentActivity.
                                    findViewById( R.id.errorCounter );
            failureCounterText = (TextView)parentActivity.
                                    findViewById( R.id.failureCounter );
            Log.d( LOG_TAG, "Test started" );
            AndroidTestRunner testRunner = new AndroidTestRunner();
            testRunner.setTest( new ExampleSuite() );
            testRunner.addTestListener( this );
            testRunner.setContext( parentActivity );
            testRunner.runTest();
            Log.d( LOG_TAG, "Test ended" );
        }

// TestListener
        public void addError(Test test, Throwable t) 
        {
            Log.d( LOG_TAG, "addError: "+test.getClass().getName() );
            Log.d( LOG_TAG, t.getMessage(), t );
            ++errorCounter;
            TestDisplay td = new TestDisplay(
                    TestDisplay.displayEvent.ERROR,
                    test.getClass().getName(),
                    testCounter,
                    errorCounter,
                    failureCounter,
                    statusText,
                    testCounterText,
                    errorCounterText,
                    failureCounterText );
            parentActivity.runOnUiThread( td );
        }

        public void addFailure(Test test, AssertionFailedError t) 
        {
            Log.d( LOG_TAG, "addFailure: "+test.getClass().getName() );
            Log.d( LOG_TAG, t.getMessage(), t );
            ++failureCounter;
            TestDisplay td = new TestDisplay(
                    TestDisplay.displayEvent.FAILURE,
                    test.getClass().getName(),
                    testCounter,
                    errorCounter,
                    failureCounter,
                    statusText,
                    testCounterText,
                    errorCounterText,
                    failureCounterText );
            parentActivity.runOnUiThread( td );
        }

        public void endTest(Test test) 
        {
            Log.d( LOG_TAG, "endTest: "+test.getClass().getName() );
            TestDisplay td = new TestDisplay(
                    TestDisplay.displayEvent.END_TEST,
                    test.getClass().getName(),
                    testCounter,
                    errorCounter,
                    failureCounter,
                    statusText,
                    testCounterText,
                    errorCounterText,
                    failureCounterText );
            parentActivity.runOnUiThread( td );
        }

        public void startTest(Test test)
        {
            Log.d( LOG_TAG, "startTest: "+test.getClass().getName() );
            ++testCounter;
            TestDisplay td = new TestDisplay(
                    TestDisplay.displayEvent.START_TEST,
                    test.getClass().getName(),
                    testCounter,
                    errorCounter,
                    failureCounter,
                    statusText,
                    testCounterText,
                    errorCounterText,
                    failureCounterText );
            parentActivity.runOnUiThread( td );
        }
}
整个程序的核心代码,如下:

 AndroidTestRunner testRunner = new AndroidTestRunner();
            testRunner.setTest( new ExampleSuite() );
            testRunner.addTestListener( this );
            testRunner.setContext( parentActivity );
            testRunner.runTest();
AndroidTestRunner这个核心类,在前面的篇幅中我们已经学习过,再次回忆下这张图(在大脑中留下深刻的记忆,后面会经常使用):


        红色划线部分代表例子程序代码中使用的AndroidTestRunner类的函数。这里使用单独线程的主要作用就是:testRunner.runTest();会占用大量的时间,如果直接在UI线程中运行会阻滞UI线程,导致界面停止反应,这对用户的操作会有很大的影响。
        如何将TestRunner 中的测试信息显示在界面上?
       Android SDK为我们提供了Handler,通过Handler与一个线程的消息队列相关联,发送和处理信息。在这个例子中使用了Activity类的runOnUiThread (Runnable action)函数,这个函数的主要功能:在UI线程中运行指定的操作,如果当前线程是UI线程,然后采取行动立即执行;如果当前线程不是UI线程,发送消息到UI线程的事件队列。
整个程序就介绍完了,运行程序后的界面如下:


在这里需要特殊说明的是:打开AndroidManifest.xml文件,发现<application>有个以前没有见过的标记,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="aexp.junit"
      android:versionCode="1"
      android:versionName="1.0.0">
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <application android:label="@string/app_name">
        <uses-library android:name="android.test.runner"/>
        <activity android:name=".JUnit"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
   <uses-sdk android:minSdkVersion="4" />
</manifest> 
总结说明
这个例子已经学习完了,虽然它比较简单,但是让我们清晰的了解如何使用AndroidTestRunner,后面我们将继续介绍一些复杂的例子,更加深入的学习。

参考文献:
Android、JUnit深入浅出系列文章


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值