2011-11-13

在 Android 使用單元測試(Unit Test)

在 Android 也可以使用單元測試,不過倒不是用 jUnit,而是用 Android 本身提供的 android.test package。

先來建立測試標的,以一個簡單的位元計算器作為測試目標。

BitOperatorActivity
public class BitOperatorActivity extends Activity implements OnClickListener {

    private static final String TAG = "BitOperatorActivity";
    private static final String BIT_OR = "|";
    private static final String BIT_NOT = "^";
    private static final String BIT_AND = "&";
    private List<String> operatorList = new ArrayList<String>();
    private String aOperand;
    private String bOperand;
    private String operator;
    private TextView aTv;
    private TextView bTv;
    private TextView operatorTv;
    private EditText cOperand;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        this.aTv = (TextView) this.findViewById(R.id.a);
        this.bTv = (TextView) this.findViewById(R.id.b);
        this.operatorTv = (TextView) this.findViewById(R.id.operator);
        this.cOperand = (EditText) this.findViewById(R.id.c);
        ((Button) this.findViewById(R.id.btn)).setOnClickListener(this);
        this.operatorList.add(BitOperatorActivity.BIT_AND);
        this.operatorList.add(BitOperatorActivity.BIT_NOT);
        this.operatorList.add(BitOperatorActivity.BIT_OR);
    }

    @Override
    protected void onResume() {
        super.onResume();
        this.newGame();
    }

    private void newGame() {
        int a = (int) (100 * Math.random());
        int b = (int) (100 * Math.random());
        this.aOperand = Integer.toBinaryString(a);
        this.bOperand = Integer.toBinaryString(b);
        Collections.shuffle(this.operatorList);
        this.operator = this.operatorList.get(0);
        this.aTv.setText(this.aOperand);
        this.bTv.setText(this.bOperand);
        this.operatorTv.setText(this.operator);
        this.cOperand.setText("");
    }

    /** for test only */
    public String getOperator() {
        return this.operator;
    }

    public String bitOperate(String a, String b) {
        int aInt = Integer.parseInt(a, 2);
        int bInt = Integer.parseInt(b, 2);
        int cInt;
        if (this.operator.equals(BIT_AND)) {
            Log.d(TAG, "And operator");
            cInt = aInt & bInt;
        }
        else if (this.operator.equals(BIT_NOT)) {
            Log.d(TAG, "NOt operator");
            cInt = aInt ^ bInt;
        }
        else if (this.operator.equals(BIT_OR)) {
            Log.d(TAG, "Or operator");
            cInt = aInt | bInt;
        }
        else {
            Log.e(TAG, "Unknown operator: " + this.operator);
            Toast.makeText(this, "程式錯誤!", Toast.LENGTH_LONG).show();
            return null;
        }
        return Integer.toString(cInt, 2);
    }

    @Override
    public void onClick(View v) {
        Log.d(TAG, "onclick..." + v.getId());
        switch (v.getId()) {
        case R.id.btn:
            String answer = this.cOperand.getText().toString();
            String rightAnswer = this.bitOperate(this.aOperand, this.bOperand);
            Log.d(TAG, "Right answer: " + rightAnswer);
            if (rightAnswer.equals(answer)) {
                Log.d(TAG, "You are right!");
                Toast.makeText(this, "你答對了!", Toast.LENGTH_LONG).show();
                this.newGame();
            }
            else {
                Log.d(TAG, "You are wrong!");
                Toast.makeText(this, "哇咧!你答錯了,再試一次!", Toast.LENGTH_LONG).show();
            }
            break;
        }
    }
}
main.xml
<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:id="@+id/a"
      android:layout_width="60px" 
      android:layout_height="wrap_content" 
      android:layout_gravity="center_horizontal"
      android:gravity="right"
      />
    <TextView  
        android:id="@+id/operator"
      android:layout_width="wrap_content" 
      android:layout_height="wrap_content" 
      android:layout_gravity="center_horizontal"
      android:gravity="center"
      android:text="&"
      />
    <TextView  
        android:id="@+id/b"
      android:layout_width="60px" 
      android:layout_height="wrap_content" 
      android:layout_gravity="center_horizontal"
      android:gravity="right"
      />
    <TextView  
      android:layout_width="wrap_content" 
      android:layout_height="30px" 
      android:layout_gravity="center_horizontal"
      android:gravity="center"
      android:text="等於"
      />
    <EditText  
        android:id="@+id/c"
      android:layout_width="100px" 
      android:layout_height="wrap_content" 
      android:layout_gravity="center_horizontal"
      android:gravity="right"
      />
    <Button  
        android:id="@+id/btn"
      android:layout_width="fill_parent" 
      android:layout_height="wrap_content" 
      android:gravity="center"
      android:text="GO"
      />
</LinearLayout>
執行畫面如下。


再來建立單元測試,Android 建議使用另一個 Project 作為測試用,真要跟 Target project 放在一起好像也可以。


再來有兩個要特別注意的地方。


再來建立 Testcase,特別注意 Superclass 的設定。


BitOperatorActivityTest
public class BitOperatorActivityTest extends
        ActivityInstrumentationTestCase2<BitOperatorActivity> {

    public BitOperatorActivityTest() {
        super("idv.neil.bitOperator", BitOperatorActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
    }

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

    public void testBitOperate() {
        BitOperatorActivity act = this.getActivity();
        int aInt = 48;
        int bInt = 16;
        String a = Integer.toBinaryString(aInt);
        String b = Integer.toBinaryString(bInt);
        String c = act.bitOperate(a, b);
        int cInt = Integer.parseInt(c, 2);
        String operator = act.getOperator();
        assertNotNull(operator);
        int answer = 0;
        if ("&".equals(operator)) {
            answer = aInt & bInt;
        }
        else if ("|".equals(operator)) {
            answer = aInt | bInt;
        }
        else if ("^".equals(operator)) {
            answer = aInt ^ bInt;
        }
        else {
            assertFalse(false);
        }
        assertEquals(answer, cInt);
    }
}
完成後就可以使用熟悉的步驟來進行測試了!


特別的是,Android JUnit Test 會自動啟動或沿用已起動的 Android Emulator 來安裝並測試。


經過好一下子後(因為要啟動 Android Emulator),終於看到閃亮亮的綠色結果了。

沒有留言:

張貼留言