【IT168技術】在本文中,你將會學習到如何在Eclipse中創建Android JUnit的單元測試工程以及在不同的條件下創建及運行自動測試用例。
準備工作
本文假設讀者已經有一定的Android基礎知識,並且已經安裝了Eclipse和Android SDK等開發工具。本文將指導讀者如何將Android Junit框架應用到Android應用中去。本文還特別重點展示了如何測試Android中的Activity和如何識別程序中的錯誤。
本文的示例代碼可以在http://code.google.com/p/simple-calc-unit-testing/中下載
步驟1 被測試的應用SimpleCalc概況
在本文中,將以一個寫好了的應用SimpleCalc簡單計算器為例子進行講解。這個簡單計算器有兩個功能,允許用戶輸入兩個數並將它們相加或相乘,最後顯示結果,如下圖所示:
▲
步驟2 SimpleCalc的的界面設計
由於應用比較簡單,只佔一屏,所以我們在/res/layout/main.xml中設計如下代碼所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content" android:text="@string/hello"
android:gravity="center_horizontal" android:textSize="48px"
android:padding="12px" />
<EditText android:layout_height="wrap_content" android:id="@+id/value1"
android:hint="@string/hint1" android:inputType="numberDecimal"
android:layout_width="fill_parent" android:textSize="48px"></EditText>
<EditText android:layout_height="wrap_content" android:id="@+id/value2"
android:hint="@string/hint2" android:inputType="numberDecimal"
android:layout_width="fill_parent" android:textSize="48px"></EditText>
<FrameLayout android:id="@+id/FrameLayout01"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:padding="12px" android:background="#ff0000">
<LinearLayout android:id="@+id/LinearLayout02"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:background="#000000"
android:padding="4px">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="@string/resultLabel"
android:textSize="48px" android:id="@+id/resultLabel"></TextView>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:id="@+id/result"
android:textSize="48px" android:textStyle="bold"
android:layout_marginLeft="16px"></TextView>
</LinearLayout>
</FrameLayout>
<LinearLayout android:id="@+id/LinearLayout03"
android:layout_height="wrap_content" android:layout_width="fill_parent">
<Button android:layout_height="wrap_content" android:id="@+id/addValues"
android:text="@string/add" android:textSize="32px"
android:layout_width="wrap_content"></Button>
<Button android:layout_height="wrap_content" android:id="@+id/multiplyValues"
android:text="@string/multiply" android:textSize="32px"
android:layout_width="wrap_content"></Button>
</LinearLayout>
</LinearLayout>
簡單解析一下這個界面設計,我們使用了LinearLayout,以使得控制項能在垂直方向豎向排列。界面中包括了顯示標題「Unit Testing Sample」的textview,兩個輸入數字的edittext控制項,一個FrameLayout控制項中包含了一個水平的LinearLayout,在這個LinearLayout包含了一個顯示結果的textview以及其提示文字「Result」,注意的是FrameLayout的背景顏色設置為紅色,而LinearLayou設置成了黑色背景。
步驟3 SimpleCale Activity
本程序中只有一個Actity:MainActity.java,代碼如下:
package com.mamlambo.article.simplecalc;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
final String LOG_TAG = "MainScreen";
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final EditText value1 = (EditText) findViewById(R.id.value1);
final EditText value2 = (EditText) findViewById(R.id.value2);
final TextView result = (TextView) findViewById(R.id.result);
Button addButton = (Button) findViewById(R.id.addValues);
addButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
try {
int val1 = Integer.parseInt(value1.getText().toString());
int val2 = Integer.parseInt(value2.getText().toString());
Integer answer = val1 + val2;
result.setText(answer.toString());
} catch (Exception e) {
Log.e(LOG_TAG, "Failed to add numbers", e);
}
}
});
Button multiplyButton = (Button) findViewById(R.id.multiplyValues);
multiplyButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
try {
int val1 = Integer.parseInt(value1.getText().toString());
int val2 = Integer.parseInt(value2.getText().toString());
Integer answer = val1 * val2;
result.setText(answer.toString());
} catch (Exception e) {
Log.e(LOG_TAG, "Failed to multiply numbers", e);
}
}
});
}
}
上面的代碼十分簡單,分別在兩個按鈕的onclick事件中,對用戶輸入的數進行了相加和相乘,看上去代碼似乎沒問題,但接下來,我們將通過Junit去發現其中的bug。
步驟4 創建Android 單元測試工程
可以有兩種方法去增加單元測試工程:一種是在創建新的Android工程時,在創建嚮導時同時創建單元測試工程,另外是針對已有的項目工程添加一個單元測試工程。本文由於已經有了一個項目工程,所以用如下步驟增加單元測試工程:
▲
在Eclipse中,選擇存在的工程SimpleCalc,滑鼠右鍵後在彈出的菜單中選擇Android Tools-àNew Test Project,如下圖所示:
步驟5 設置測試工程
接下來需要對單元測試的工程進行設置,我們採用如下的設置方法:
l 測試工程名稱:我們採用SimpleCalcTest
l 工程的位置:這個可以隨便設置
l 選擇被測試的工程:這裡我們選擇已經存在的SimpleCalc
l 構建的目標版本:這裡我們選擇Android 2.1
l 測試用例的包名:設置為com.mamlambo.article.simplecalc.test,
l 設置界面如下圖所示。
▲
步驟6 SimpleCalcTest單元測試項目的結構
我們審視下SimpleCalcTest的項目結構如下圖所示,可以看到這跟普通的Android工程沒什麼兩樣:
▲
步驟7 創建單元測試用例
下面創建第一個單元測試用例,滑鼠右鍵點擊simplecalc.test的包,在彈出的菜單中選擇NewàJUnit Test Case,如下圖所示:
▲
步驟8 設置單元測試用例
接下來對單元測試進行如下設置
l 設置選擇使用Junit 3
l 原始碼目錄:這裡要設置為SimpleCalcTest工程的代碼目錄
l Package:這裡設置為com.mamlambo.article.simplecalc.test,
l 測試用例名稱:設置為MathValidation
l 測試的父類:這裡選擇「android.test.ActivityInstrumentationTestCase2.",這個是用來測試activity的Android的測試用例
l 將多選框中的setup,constructor兩個都勾選上
如下圖所示
▲
步驟9 查看MatthValidation測試用例
在上圖中,點」Finish」按鈕後,MathVlidatiton.java測試用例就創建了。在單元測試中包括如下幾個部分:construction, setUp(), 針對方法的測試用例, tearDown(), 和destruction。在setup()方法中,主要是實現一些在測試工作前的資源及環境設置等的初始化設置;而針對方法的測試用例中,需要用戶自己編寫,一般是以「test+方法名」;而tearDown()在每個測試方法之後運行,用來撤消其初始化的測試環境。
代碼如下:
package com.mamlambo.article.simplecalc.test;
import android.test.ActivityInstrumentationTestCase2;
public class MathValidation extends
ActivityInstrumentationTestCase2<MainActivity> {
public MathValidation(String name) {
super(name);
}
protected void setUp() throws Exception {
super.setUp();
}
}
步驟10 修改MathValidation的構造函數
在測試用例的構造函數中,寫入如下代碼,以將我們正在使用的測試父類與測試環境設置進行綁定。
public MathValidation() {
super("com.mamlambo.article.simplecalc", MainActivity.class);
}
步驟11 編寫setUp方法
現在可以收集數據去驗證SimpleCalc的計算方法了。在setUp方法中,首先應該通過getActivity()方法獲得當前的Activity,如下所示:
MainActivity mainActivity = getActivity();
接著,需要獲得名為R.id.result的textview控制項的實例,這個控制項實際上保存計算器應用的運算結果的,代碼如下所示:
package com.mamlambo.article.simplecalc.test;
import android.test.ActivityInstrumentationTestCase2;
import android.widget.TextView;
import com.mamlambo.article.simplecalc.MainActivity;
import com.mamlambo.article.simplecalc.R;
public class MathValidation extends ActivityInstrumentationTestCase2<MainActivity> {
private TextView result;
public MathValidation() {
super ("com.mamlambo.article.simplecalc", MainActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
MainActivity mainActivity = getActivity();
result = (TextView) mainActivity.findViewById(R.id.result);
}
}
步驟12 SimpleCalc計算器中的加法測試用例
我們首先針對SimpleCalc中的加法進行測試用例的編寫。這個測試用例中,會輸入兩個數(24和74),並測試是否其結果等於98。為了模擬在輸入數字後點按鈕的效果,我們使用了sendkeys方法,這個方法的優點在於可以在輸入後自動將焦點切換到下一個控制項上。最後,我們使用assertTrue的斷言去判斷實際結果是否就是等於98,代碼如下:
private static final String NUMBER_24 = "2 4 ENTER ";
private static final String NUMBER_74 = "7 4 ENTER ";
private static final String ADD_RESULT = "98";
public void testAddValues() {
sendKeys(NUMBER_24);
// now on value2 entry
sendKeys(NUMBER_74);
// now on Add button
sendKeys("ENTER");
// get result
String mathResult = result.getText().toString();
assertTrue("Add result should be 98", mathResult.equals(ADD_RESULT));
}
步驟13 改進測試用例
由於每次測試時,其實都是使用同一個activity的,因此在每次測試時不需要清除舊的值,我們可以在一個sendKeys()方法中,發送一系列的輸入命令,如下所示:
sendKeys(NUMBER_24 + NUMBER_74 + "ENTER");
我們測試一個小數的情況如下,看結果是否等於79.5
public void testAddDecimalValues() {
sendKeys(NUMBER_5_DOT_5 + NUMBER_74 + "ENTER");
String mathResult = result.getText().toString();
assertTrue("Add result should be " + ADD_DECIMAL_RESULT + " but was "
+ mathResult, mathResult.equals(ADD_DECIMAL_RESULT));
}
同樣,我們去編寫乘法的單元測試用例,這裡我們繼續使用sendKeys()方法,由於乘法的按鈕就在加法的按鈕右邊,所以我們在用sendkey模擬輸入了兩個數後,發送「DRAD_RIGHT」的消息,就可以了。
public void testMultiplyValues() {
sendKeys(NUMBER_24+NUMBER_74+ " DPAD_RIGHT ENTER");
String mathResult = result.getText().toString();
assertTrue("Multiply result should be " + MULTIPLY_RESULT + " but was "
+ mathResult, mathResult.equals(MULTIPLY_RESULT));
}
步驟14 在模擬器中運行單元測試
運行單元測試的方法很簡單,滑鼠右鍵項目,在彈出的菜單中選擇「Debug ASàAndroid JUnit Test」即可,運行結果如下兩圖所示:
▲
▲
其中紅色的表示測試沒辦法通過,綠色的條狀表示測試已經通過。
步驟15 Android中對屏幕顯示的單元測試
在Android 的單元測試中,還可以針對界面的顯示位置等進行單元測試。比如我們在Eclipse時開發採用的界面模擬器是在800*480的模式下的,但如果在其他尺寸規格的行動裝置上是否能正常運行呢?這就需要對界面設置部分進行單元測試了。
我們另外創建一個單元測試用例,用前文所講的方法新建立一個名為LayoutTests的單元測試用例,如下圖:
▲
並編寫如下代碼:
package com.mamlambo.article.simplecalc.test;
import android.test.ActivityInstrumentationTestCase2;
import android.view.View;
import android.widget.Button;
import com.mamlambo.article.simplecalc.MainActivity;
import com.mamlambo.article.simplecalc.R;
public class LayoutTests extends ActivityInstrumentationTestCase2 {
private Button addValues;
private Button multiplyValues;
private View mainLayout;
public LayoutTests() {
super("com.mamlambo.article.simplecalc", MainActivity.class);
}
protected void setUp() throws Exception {
super.setUp();
MainActivity mainActivity = getActivity();
addValues = (Button) mainActivity.findViewById(R.id.addValues);
multiplyValues = (Button) mainActivity
.findViewById(R.id.multiplyValues);
mainLayout = (View) mainActivity.findViewById(R.id.mainLayout);
}
}
這裡,分別獲得了加法按鈕和乘法按鈕的實例。接下來,增加一個testAddButtonOnScreen
的方法,以測試按鈕的位置是否正確。在這個方法中,首先你要決定屏幕的大小。有很多方
法去檢測屏幕的大小,比如用getWidth()和getHeight()方法,當然在考慮尺寸時,還必須考
慮象標題欄,狀態欄等所佔用的位置大小。下面是其代碼:
public void testAddButtonOnScreen() {
int fullWidth = mainLayout.getWidth();
int fullHeight = mainLayout.getHeight();
int[] mainLayoutLocation = new int[2];
mainLayout.getLocationOnScreen(mainLayoutLocation);
int[] viewLocation = new int[2];
addValues.getLocationOnScreen(viewLocation);
Rect outRect = new Rect();
addValues.getDrawingRect(outRect);
assertTrue("Add button off the right of the screen", fullWidth
+ mainLayoutLocation[0] > outRect.width() + viewLocation[0]);
assertTrue("Add button off the bottom of the screen", fullHeight
+ mainLayoutLocation[1] > outRect.height() + viewLocation[1]);
}
在各類尺寸的模擬器上運行,可以得到如下結果所示的測試結果:
480x800, portrait 模式 (通過)
800x480, landscape mode (失敗)
320x480, portrait mode (失敗)
480x320, landscape (失敗)
480x854, portrait mode (通過)
854x480, landscape mode (失敗)?
大家可以思考下為什麼有的測試用例成功有的失敗。
總結
本文講解了如何使用junit配合Android的應用進行單元測試及詳細步驟,以及如何在
Junit測試Android時的小技巧。可以看到,在設計完應用後應該編寫單元測試用例,測試用
例越多和越詳細,則對程序的正確性提高越有好處。