使用Junit對Android應用進行單元測試

http://www.uml.org.cn/mobiledev/201305242.asp


在本文中,你將會學習到如何在Eclipse中創建Android                          JUnit的單元測試工程以及在不同的條件下創建及運行自動測試用例。

準備工作

本文假設讀者已經有一定的Android基礎知識,並且已經安裝了Eclipse和Android                          SDK等開發工具。本文將指導讀者如何將Android Junit框架應用到Android應用中去。本文還特別重點展示瞭如何測試Android中的Activity和如何識別程序中的錯誤。

步驟1 被測試的應用SimpleCalc概況

在本文中,將以一個寫好了的應用SimpleCalc簡單計算器爲例子進行講解。這個簡單計算器有兩個功能,允許用戶輸入兩個數並將它們相加或相乘,最後顯示結果,如下圖所示:

2013052421.jpg

步驟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工程時,在創建嚮導時同時創建單元測試工程,另外是針對已有的項目工程添加一個單元測試工程。本文由於已經有了一個項目工程,所以用如下步驟增加單元測試工程:

2013052422.jpg

在Eclipse中,選擇存在的工程SimpleCalc,鼠標右鍵後在彈出的菜單中選擇Android                          Tools-àNew Test Project,如下圖所示:

步驟5 設置測試工程

接下來需要對單元測試的工程進行設置,我們採用如下的設置方法:

測試工程名稱:我們採用SimpleCalcTest

工程的位置:這個可以隨便設置

選擇被測試的工程:這裏我們選擇已經存在的SimpleCalc

構建的目標版本:這裏我們選擇Android 2.1

測試用例的包名:設置爲com.mamlambo.article.simplecalc.test,

設置界面如下圖所示:

2013052423.jpg

步驟6 SimpleCalcTest單元測試項目的結構

我們審視下SimpleCalcTest的項目結構如下圖所示,可以看到這跟普通的Android工程沒什麼兩樣:

2013052424.jpg

步驟7 創建單元測試用例

下面創建第一個單元測試用例,鼠標右鍵點擊simplecalc.test的包,在彈出的菜單中選擇NewàJUnit                          Test Case,如下圖所示:

2013052425.jpg

步驟8 設置單元測試用例

接下來對單元測試進行如下設置

設置選擇使用Junit 3

源代碼目錄:這裏要設置爲SimpleCalcTest工程的代碼目錄

Package:這裏設置爲com.mamlambo.article.simplecalc.test,

測試用例名稱:設置爲MathValidation

測試的父類:這裏選擇“android.test.ActivityInstrumentationTestCase2.",這個是用來測試activity的Android的測試用例

將多選框中的setup,constructor兩個都勾選上

如下圖所示

2013052426.jpg

步驟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 {   

   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 {

   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”即可,運行結果如下兩圖所示:

2013052427.jpg

2013052428.jpg

其中紅色的表示測試沒辦法通過,綠色的條狀表示測試已經通過。

步驟15 Android中對屏幕顯示的單元測試

在Android 的單元測試中,還可以針對界面的顯示位置等進行單元測試。比如我們在Eclipse時開發採用的界面模擬器是在800*480的模式下的,但如果在其他尺寸規格的移動設備上是否能正常運行呢?這就需要對界面設置部分進行單元測試了。

我們另外創建一個單元測試用例,用前文所講的方法新建立一個名爲LayoutTests的單元測試用例,如下圖:

2013052429.jpg

並編寫如下代碼:

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時的小技巧。可以看到,在設計完應用後應該編寫單元測試用例,測試用例越多和越詳細,則對程序的正確性提高越有好處。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章