1. 程式人生 > >通過JUnit進行Android單元測試

通過JUnit進行Android單元測試

2個 nvi frame oncreate protected [] 命令 lib 是我

要了解android單元測試,首先必須了解junit

什麽是 JUnit ?

JUnit是采用測試驅動開發的方式,也就是說在開發前先寫好測試代碼,主要用來說明被測試的代碼會被如何使用,錯誤處理等;然後開始寫代碼,並在測試代碼中逐步測試這些代碼,直到最後在測試代碼中完全通過。

現簡要說JUnit的4大功能

  1. 管理測試用例。修改了哪些代碼,這些代碼的修改會對哪些部分有影響,通過JUnit將這次的修改做個完整測試。這也就JUnit中所謂的TestSuite。
  2. 定義測試代碼。這也就是JUnit中所謂的TestCase,根據源代碼的測試需要定義每個TestCase,並將TestCase添加到相應的TestSuite方便管理。
  3. 定義測試環境。在TestCase測試前會先調用“環境”配置,在測試中使用,當然也可以在測試用例中直接定義測試環境。
  4. 檢測測試結果。對於每種正常、異常情況下的測試,運行結果是什麽、結果是否是我們預期的等都需要有個明確的定義,JUnit在這方面提供了強大的功能。

以上部分與我們平常使用IDE調試的過程是完全一樣的,只不過是增加了測試用例管理、測試結果檢測等功能,提高了單元的效率,保證了單元測試的完整性,明確了單元測試的目標。

JUnit 基本原理

一個 JUnit 測試包含以下元素:

表 1. 測試用例組成

開發代碼部分 測試代碼部分 測試工具部分
待測試類 A 通過擴展 TestCase 或者構造 TestSuit 方法
編寫測試類 B
一個測試運行器(TestRunner)R,可以選擇圖形界面或文本界面

操作步驟:

將 B 通過命令行方式或圖形界面選擇方式傳遞給 R,R 自動運行測試,並顯示結果。

首先看下junit測試類庫和android中單元測試類庫:

SDK 功能說明
junit.framework JUnit測試框架
junit.runner 實用工具類支持JUnit測試框架
android.test Android 對JUnit測試框架的擴展包
android.test.mock Android的一些輔助類
android.test.suitebuilder 實用工具類,支持類的測試運行

在這些包中最為重要的是: junit.framework、 android.test,其中前者是JUnit的核心包,後者是Andoid SDK在Junit.framework的基礎上擴展出來的包,我們將重點解析這2個包。

junit.framework:

技術分享

  1. TestSuit:TestSuite是測試用例的集合;
  2. TestCase:定義運行多個測試用例;
  3. TestResult:收集一個測試案例的結果,測試結果分為失敗和錯誤,如果未能預計的斷言就是失敗,錯誤就像一個ArrayIndexOutOfBoundsException異常而導致的無法預料的問題;
  4. TestFailure:測試失敗時捕獲的異常;
  5. Assert:斷言的方法集,當斷言失敗時顯示信息;

TestCase與TestSuite之間的關系,有些類似於圖元對象與容器對象之間的關系

android.test包:

技術分享

JUnit TestCase類

繼承自JUnit的TestCase,不能使用Instrumentation框架。但這些類包含訪問系統對象(如Context)的方法。使用Context,你可以瀏覽資源,文件,數據庫等等。基類是AndroidTestCase,一般常見的是它的子類,和特定組件關聯。

子類有:

l ApplicationTestCase——測試整個應用程序的類。它允許你註入一個模擬的Context到應用程序中,在應用程序啟動之前初始化測試參數,並在應用程序結束之後銷毀之前檢查應用程序。

l ProviderTestCase2——測試單個ContentProvider的類。因為它要求使用MockContentResolver,並註入一個IsolatedContext,因此Provider的測試是與OS孤立的。

l ServiceTestCase——測試單個Service的類。你可以註入一個模擬的Context或模擬的Application(或者兩者),或者讓Android為你提供Context和MockApplication。

Instrumentation TestCase類

繼承自JUnit TestCase類,並可以使用Instrumentation框架,用於測試Activity。使用Instrumentation,Android可以向程序發送事件來自動進行UI測試,並可以精確控制Activity的啟動,監測Activity生命周期的狀態。

基類是InstrumentationTestCase。它的所有子類都能發送按鍵或觸摸事件給UI。子類還可以註入一個模擬的Intent。

子類有:

l ActivityTestCase——Activity測試類的基類。

l SingleLaunchActivityTestCase——測試單個Activity的類。它能觸發一次setup()和tearDown(),而不是每個方法調用時都觸發。如果你的測試方法都是針對同一個Activity的話,那就使用它吧。

l SyncBaseInstrumentation——測試Content Provider同步性的類。它使用Instrumentation在啟動測試同步性之前取消已經存在的同步對象。

l ActivityUnitTestCase——對單個Activity進行單一測試的類。使用它,你可以註入模擬的Context或Application,或者兩者。它用於對Activity進行單元測試。

不同於其它的Instrumentation類,這個測試類不能註入模擬的Intent。

l ActivityInstrumentationTestCase2——在正常的系統環境中測試單個Activity的類。你不能註入一個模擬的Context,但你可以註入一個模擬的Intent。另外,你還可以在UI線程(應用程序的主線程)運行測試方法,並且可以給應用程序UI發送按鍵及觸摸事件。

下面找了幾個例子:

首先看junit,然後在看android test:

junit:

待測試類A:

package cn.edu.wtu.junit;

public class Calcuator {
    public double add(double n1, double n2) {
        return n1 + n1;
    }
}


測試代碼B:擴展testcase

package cn.edu.wtu.junit;

import junit.framework.TestCase;

public class TestCalcuator extends TestCase {
    public void testAdd(){
        Calcuator calcuator=new Calcuator();
        double result=calcuator.add(1,2);
        assertEquals(3,result,0);
    }

}
testsuit 測試容器:
package cn.edu.wtu.junit;

import junit.framework.Test;
import junit.framework.TestSuite;
import junit.textui.TestRunner;

public class TestAll extends TestSuite {
    public static Test suite() {
        TestSuite suite = new TestSuite("TestSuite Test");
        suite.addTestSuite(TestCalcuator.class);
        suite.addTestSuite(TestCalcuator2.class);
        return suite;
    }
    public static void main(String args[]){
//    	命令行輸出 測試工具 一個測試運行器
        TestRunner.run(suite());
    }
}


run on junit 圖形界面顯示:

技術分享

run java application:控制臺輸出:

技術分享

android test :

首先看下非instrumentation框架測試:

測試代碼:

package aexp.junit;

import android.test.AndroidTestCase;
import android.util.Log;

public class MathTest extends AndroidTestCase 
{
    protected int i1;
    protected int i2;
    static final String LOG_TAG = "MathTest";

    public void setUp() 
    {
        i1 = 2;
        i2 = 3;    
    }

    public void testAdd() 
    {
        Log.d( LOG_TAG, "testAdd" );
        assertTrue( LOG_TAG+"1", ( ( i1 + i2 ) == 5 ) );
    }

    public void testAndroidTestCaseSetupProperly()
    {
        super.testAndroidTestCaseSetupProperly();
        Log.d( LOG_TAG, "testAndroidTestCaseSetupProperly" );
    }
}

package aexp.junit;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.util.Log;
import android.test.AndroidTestCase;

public class ContactTest extends AndroidTestCase 
{
    static final String LOG_TAG = "ContactTest";
    static final String TESTUSER_NAME = "Test User";
    static final String TESTUSER_NOTES = "Test note";
    ContentResolver contentResolver;
    Uri newPerson;

    public void setUp()
    {
        contentResolver = getContext().getContentResolver();
        ContentValues person = new ContentValues();
        person.put(Contacts.People.NAME, TESTUSER_NAME );
        person.put(Contacts.People.NOTES, TESTUSER_NOTES );

        newPerson = contentResolver.insert(
                    Contacts.People.CONTENT_URI,person);
    }

    public void testInsertContact() 
    {
        Log.d( LOG_TAG, "testInsertContact" );
        assertNotNull( newPerson );
    }

    public void testQueryContact() 
    {
        Log.d( LOG_TAG, "testQueryContact" );
    	String columns[] = { Contacts.People.NAME,
                            Contacts.People.NOTES };
        Cursor c = contentResolver.query( Contacts.People.CONTENT_URI,
                    columns, 
                    Contacts.People.NAME+"=?",
                    new String[] { TESTUSER_NAME },
                    null );
        assertNotNull( c );
        int hits = 0;
        while( c.moveToNext() )
        {
            int nameColumnIndex = c.getColumnIndex( Contacts.People.NAME );
            int notesColumnIndex = c.getColumnIndex( Contacts.People.NOTES );
	        String name = c.getString( nameColumnIndex );
            String notes = c.getString( notesColumnIndex );
            Log.d( LOG_TAG,"retrieved name: "+name );
            Log.d( LOG_TAG,"retrieved notes: "+notes );
            assertEquals( TESTUSER_NAME, name );
            assertEquals( TESTUSER_NOTES, notes );
            ++hits;
        }
        assertEquals( hits,1 );
        c.close();
    }

    public void tearDown() 
    {
        contentResolver.delete( newPerson, null, null );
    }
}


子樹:

package aexp.junit;

import junit.framework.TestSuite;
import junit.framework.Assert;

public class SomeTest extends TestSuite {
	public void testSomething() throws Throwable
	{
	       Assert.assertTrue(1 + 1 == 2);
	}

	public void testSomethingElse() throws Throwable
	{
	       Assert.assertTrue(1 + 1 == 3);
	}
}


測試樹:

package aexp.junit;

import junit.framework.TestSuite;

public class ExampleSuite extends TestSuite
{
    public ExampleSuite() 
    {
        addTestSuite( MathTest.class );
        addTestSuite( ContactTest.class );
        addTestSuite(SomeTest.class);
    }
}


測試運行器:

package aexp.junit;

import android.app.Activity;
import android.os.Bundle;
import android.test.AndroidTestCase;
import android.test.AndroidTestRunner;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.util.Log;
import junit.framework.TestListener;
import junit.framework.Test;
import junit.framework.AssertionFailedError;

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 );
	        }
}

運行結果:

技術分享

instrumentation框架:

首先新建一個android工程:

裏面編寫3個activity:MainActivity,HomeActivity,LoginActivity

MainActivity是加載界面,LoginActivity是登陸界面,HomeActivity是最終界面

package cn.edu.wtu.junit;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {
	
	private static final boolean DEBUG = true;
	private static final String TAG = "-- MainActivity";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		if (DEBUG) {
			Log.i(TAG, "onCreate");
		}

		super.onCreate(savedInstanceState);
		setContentView(R.layout.act_main);
		View toLoginView = findViewById(R.id.to_login);
		toLoginView.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (DEBUG) {
					Log.i(TAG, "toLoginView clicked");
				}

				Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
				startActivity(intent);
			}
		});
	}
}

package cn.edu.wtu.junit;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;

public class LoginActivity extends Activity {
	private static final boolean DEBUG = true;
	private static final String TAG = "-- LoginActivity";

	private EditText mUsernameView;
	private EditText mPasswordView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		if (DEBUG) {
			Log.i(TAG, "onCreate");
		}

		super.onCreate(savedInstanceState);
		setContentView(R.layout.act_login);
		mUsernameView = (EditText) findViewById(R.id.username);
		mPasswordView = (EditText) findViewById(R.id.password);

		View submitView = findViewById(R.id.submit);
		submitView.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (DEBUG) {
					Log.i(TAG, "submitView clicked");
				}

				Intent intent = new Intent(getApplicationContext(), HomeActivity.class);
				intent.putExtra(HomeActivity.EXTRA_USERNAME, mUsernameView.getText().toString());
				intent.putExtra(HomeActivity.EXTRA_PASSWORD, mPasswordView.getText().toString());
				startActivity(intent);
			}
		});

		View resetView = findViewById(R.id.reset);
		resetView.setOnClickListener(new View.OnClickListener() {
			public void onClick(View view) {
				if (DEBUG) {
					Log.i(TAG, "resetView clicked");
				}

				mUsernameView.setText("");
				mPasswordView.setText("");
				mUsernameView.requestFocus();
			}
		});
	}
}

package cn.edu.wtu.junit;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class HomeActivity extends Activity {
	private static final boolean DEBUG = true;
	private static final String TAG = "-- HomeActivity";

	public static final String EXTRA_USERNAME = "yuan.activity.username";
	public static final String EXTRA_PASSWORD = "yuan.activity.password";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		if (DEBUG) {
			Log.i(TAG, "onCreate");
		}
		super.onCreate(savedInstanceState);
		Intent intent = getIntent();
		StringBuilder sb = new StringBuilder();
		sb.append("username:").append(intent.getStringExtra(EXTRA_USERNAME)).append("\n");
		sb.append("password:").append(intent.getStringExtra(EXTRA_PASSWORD));

		setContentView(R.layout.act_home);
		TextView loginContentView = (TextView) findViewById(R.id.login_content);
		loginContentView.setText(sb.toString());
	}
}


然後新建一個測試工程,基於上面一個project:

package cn.edu.wtu.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 cn.edu.wtu.junit.MainActivity;

public class MainActivityTest extends ActivityInstrumentationTestCase2
   
   
   
   
   
    
    
    
    
     {
	private static final String TAG = "=== MainActivityTest";

	private Instrumentation mInstrument;
	private MainActivity mActivity;
	private View mToLoginView;

	public MainActivityTest() {
		super("cn.edu.wtu.junit", MainActivity.class);
	}

	@Override
	public void setUp() throws Exception {
		super.setUp();
		mInstrument = getInstrumentation();
		// 啟動被測試的Activity
		mActivity = getActivity();
		mToLoginView = mActivity.findViewById(cn.edu.wtu.junit.R.id.to_login);
	}

	public void testPreConditions() {
		// 在執行測試之前,確保程序的重要對象已被初始化
		assertTrue(mToLoginView != null);
	}


	
//	@UiThreadTest
//	這將會在UI線程裏運行方法裏所有的語句。不與UI交互的方法不允許這麽做
//	註意:waitForIdleSync和sendKeys不允許在UI線程裏運行
	public void testToLogin() {
		// @UiThreadTest註解使整個方法在UI線程上執行,等同於上面註解掉的代碼

//		exception
		mInstrument.runOnMainSync(new Runnable() {
			public void run() {
				mToLoginView.requestFocus();
				mToLoginView.performClick();
			}
		});
		
//		mActivity.runOnUiThread(new Runnable(){
//
//			@Override
//			public void run() {
//				
//				mToLoginView.requestFocus();
//				mToLoginView.performClick();
//			}
//			
//		});
	}

	@Suppress
	public void testNotCalled() {
		// [email protected]
		Log.i(TAG, "method ‘testNotCalled‘ is called");
	}

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

package cn.edu.wtu.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 cn.edu.wtu.junit.LoginActivity;

public class LoginActivityTest extends ActivityInstrumentationTestCase2
   
   
   
   
   
    
    
    
    
     {
	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("cn.edu.wtu.junit", LoginActivity.class);
	}

	@Override
	public void setUp() throws Exception {
		super.setUp();
		/*
		 * 關閉觸屏模式為了控制從測試程序中發送給模擬器或設備的按鍵事件,你必須關閉觸屏模式。如果你不這麽做,按鍵事件將被忽略。
		 * 你需要在調用getActivity()啟動Activity之前調用ActivityInstrumentationTestCase2.setActivityTouchMode(false)。
		 * 你必須在非UI線程中運行這個調用。基於這個原因,[email protected]tUp()中調用。
		 * 要向程序發送key事件的話,必須在getActivity之前調用該方法來關閉touch模式
		 * 否則key事件會被忽略
		 */
		setActivityInitialTouchMode(false);

		mInstrument = getInstrumentation();
		mActivity = getActivity();
		Log.i(TAG, "current activity: " + mActivity.getClass().getName());
		mUsernameView = (EditText) mActivity.findViewById(cn.edu.wtu.junit.R.id.username);
		mPasswordView = (EditText) mActivity.findViewById(cn.edu.wtu.junit.R.id.password);
		mSubmitView = mActivity.findViewById(cn.edu.wtu.junit.R.id.submit);
		mResetView = mActivity.findViewById(cn.edu.wtu.junit.R.id.reset);
	}

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

	public void testInput() {
		input();
		assertEquals("yuan", mUsernameView.getText().toString());
		assertEquals("1123", 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_Y, KeyEvent.KEYCODE_U,
				KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_N);

		// 效果同上面sendKeys之前的代碼
		mInstrument.runOnMainSync(new Runnable() {
			public void run() {
				mPasswordView.requestFocus();
			}
		});
		sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_1,
				KeyEvent.KEYCODE_2, KeyEvent.KEYCODE_3);
	}
}
   
   
   
   
   

package cn.edu.wtu.test;

import android.content.Intent;
import android.test.ActivityUnitTestCase;
import android.widget.TextView;
import cn.edu.wtu.junit.HomeActivity;

public class HomeActivityTest extends ActivityUnitTestCase
   
   
   
   
   
    
    
    
    
     {
	private static final String TAG = "=== HomeActivityTest";

	private static final String LOGIN_CONTENT = "username:yuan\npassword:1123";

	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, "yuan");
		intent.putExtra(HomeActivity.EXTRA_PASSWORD, "1123");
		// HomeActivity有extra參數,所以我們需要以intent來啟動它
		mHomeActivity = launchActivityWithIntent("cn.edu.wtu.junit", HomeActivity.class, intent);
		mLoginContentView = (TextView) mHomeActivity.findViewById(cn.edu.wtu.junit.R.id.login_content);
	}

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

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


run on android unit:

技術分享

參考:

http://www.ibm.com/developerworks/cn/java/j-lo-junit-src/

http://www.moandroid.com/?page_id=1176

http://www.ibm.com/developerworks/cn/java/j-lo-junit4/

http://www.cnblogs.com/xirihanlin/archive/2010/06/15/1758677.html

轉:http://www.open-open.com/lib/view/open1328152424546.html

通過JUnit進行Android單元測試