稍微修改昨天的測試標的,增加答對與答錯次數的紀錄,再用單元測試來做檢查。
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;
private TextView sTv;
private TextView fTv;
@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);
this.sTv = (TextView) this.findViewById(R.id.successCnt);
this.fTv = (TextView) this.findViewById(R.id.failureCnt);
((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...");
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.sTv.setText(String.valueOf(Integer.parseInt(this.sTv.getText().toString()) + 1));
this.newGame();
}
else {
Log.d(TAG, "You are wrong! Your answer is " + answer + ".");
Toast.makeText(this, "哇咧!你答錯了,再試一次!", Toast.LENGTH_LONG).show();
this.fTv.setText(String.valueOf(Integer.parseInt(this.fTv.getText().toString()) + 1));
}
break;
}
}
}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" > <LinearLayout android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" > <TextView android:id="@+id/success" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="成功:" /> <TextView android:id="@+id/successCnt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" /> <TextView android:id="@+id/failure" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text=", 失敗:" /> <TextView android:id="@+id/failureCnt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" /> </LinearLayout> <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>
再來就是兩個 UI 單元測試的重點:
Activity.runOnUiThread()
UI 的動作要在 UI Thread 裡執行,不管是改值或觸發動作,不可以在單元測試的 Thread 裡執行,但是取值就沒關係。
Instrumentation.waitForIdleSync()
runOnUiThread() 不會 hold 住目前的 thread,所以會立即往下執行,因此得呼叫 waitForIdleSync() 來等待 UI Thread 執行完成,否則會發生不可預期的狀況。
BitOperatorActivityTest
public class BitOperatorActivityTest extends
ActivityInstrumentationTestCase2<BitOperatorActivity> {
private TextView aTv;
private TextView bTv;
private TextView operatorTv;
private TextView sTv;
private TextView fTv;
private EditText cOperand;
private Button goBtn;
public BitOperatorActivityTest() {
super("idv.neil.bitOperator", BitOperatorActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
// 是用測試標的的 idv.neil.bitOperator.R,不是測試 project 的 R
this.aTv = (TextView) this.getActivity().findViewById(R.id.a);
this.bTv = (TextView) this.getActivity().findViewById(R.id.b);
this.operatorTv = (TextView) this.getActivity().findViewById(
R.id.operator);
this.sTv = (TextView) this.getActivity().findViewById(R.id.successCnt);
this.fTv = (TextView) this.getActivity().findViewById(R.id.failureCnt);
this.cOperand = (EditText) this.getActivity().findViewById(R.id.c);
this.goBtn = (Button) this.getActivity().findViewById(R.id.btn);
}
@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 {
fail("Wrong operator - " + operator);
}
assertEquals(answer, cInt);
}
public void testCount() {
// 答對
this.clickButton(this.getAnswer());
// 執行完成了,取值判斷
assertEquals("1", this.sTv.getText().toString());
assertEquals("0", this.fTv.getText().toString());
// 答錯
this.clickButton(this.getAnswer() + 1);
// 執行完成了,取值判斷
assertEquals("1", this.sTv.getText().toString());
assertEquals("1", this.fTv.getText().toString());
// 又答錯
this.clickButton(this.getAnswer() - 1);
// 執行完成了,取值判斷
assertEquals("1", this.sTv.getText().toString());
assertEquals("2", this.fTv.getText().toString());
// 總算答對
this.clickButton(this.getAnswer());
// 執行完成了,取值判斷
assertEquals("2", this.sTv.getText().toString());
assertEquals("2", this.fTv.getText().toString());
}
private int getAnswer() {
String a = this.aTv.getText().toString();
String b = this.bTv.getText().toString();
int aInt = Integer.parseInt(a, 2);
int bInt = Integer.parseInt(b, 2);
String operator = this.operatorTv.getText().toString();
final int c;
if ("&".equals(operator)) {
c = aInt & bInt;
}
else if ("|".equals(operator)) {
c = aInt | bInt;
}
else if ("^".equals(operator)) {
c = aInt ^ bInt;
}
else {
fail("Wrong operator - " + operator);
c = 0;
}
return c;
}
private void clickButton(final int c) {
// UI 的動作要在 UI Thread 裡執行
// 不管是改值或觸發動作
this.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
cOperand.setText(Integer.toString(c, 2));
goBtn.performClick();
}
});
// 等待 UI Thread 執行完成
this.getInstrumentation().waitForIdleSync();
}
}完工。Android Emulator Log:
Console Log,今天的 console log 比較短是因為 Android Emulator 已經啟動了:



沒有留言:
張貼留言