官網教學,參考 https://developer.android.com/training/testing.html
這裡為紀錄詳細步驟。
Target Class : RoundAmountSaving也就是要測試的Android app(被測目標),該app能幫你計算本金和,你只需要輸入目前儲蓄金額,年利率,以及儲蓄月數
它就能算出儲蓄多久可得到多少錢。
裡面的元件組成相當簡單,只有幾個EditText,Button,一個簡單的計算公式。
package com.foxx.round_amount_saving; import com.foxx_round_amount_saving.R; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class RoundAmountSaving extends Activity { private static final int PERCENT = 100; private static final int MONTH_COUNT = 12; private EditText mInputLend; private EditText mInputRate; private EditText mInputNumber; private Button mSubmit; private TextView mAmount; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initLayoutComponents(); } private void initLayoutComponents() { mInputLend = (EditText) findViewById(R.id.input_lend); mInputNumber = (EditText) findViewById(R.id.input_number); mInputRate = (EditText) findViewById(R.id.input_rate); mSubmit = (Button) findViewById(R.id.submit); mSubmit.setOnClickListener(getClickListenerForSubmit()); mAmount = (TextView) findViewById(R.id.amount); } private OnClickListener getClickListenerForSubmit() { return new OnClickListener() { @Override public void onClick(View v) { mAmount.setText("" + countAmount()); } }; } private double countAmount() { int lend = Integer.parseInt(mInputLend.getText().toString()); double rateOfMonth = Double.parseDouble(mInputRate.getText().toString()) / MONTH_COUNT / PERCENT; int number = Integer.parseInt(mInputNumber.getText().toString()); return lend * (Math.pow(rateOfMonth + 1, number)); } }
開始建立Android Test Project
1. Eclipse -> File -> New -> Other -> type : android test project -> next
2. type : RoundAmountSavingTest in project name -> next
3. select test target : RoundAmountSaving -> Finish
Note : RoundAmountSaving 是 project Name
完成後會產生android test project,名稱為 RoundAmountSavingTest,其中有設定好的package,接著開始來寫 test case
4.建立 junit test case
Eclipse -> File -> New -> Other -> type : junit test case -> next
5. 選擇 New Junit 3 test,輸入Name,並選擇Class under test : com.foxx.roundamountsaving.RoundAmountSaving -> Finish
6.修改RoundAmountSavingTest.java
由於RoundAmountSaving繼承Activity,為了測試Activity我們必須讓Test Case繼承ActivityInstrumentationTestCase2並呼叫父類別建構式
package com.foxx.roundamountsaving.test; import android.test.ActivityInstrumentationTestCase2; import com.foxx.roundamountsaving.RoundAmountSaving; public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving> { public RoundAmountSavingTest(){ super(RoundAmountSaving.class); } }
接著override setUp方法,並在方法中設定相關內容,包含測試類別的實體
package com.foxx.roundamountsaving.test; import android.test.ActivityInstrumentationTestCase2; import com.foxx.roundamountsaving.RoundAmountSaving; public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving> { private RoundAmountSaving mRoundAmountSaving; public RoundAmountSavingTest(){ super(RoundAmountSaving.class); } @Override protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mRoundAmountSaving = getActivity(); } }
可以開始寫測試方法了,為了確保測試資料的完整性,先測試mRoundAmountSaving不可為null
public void testRoundAmountSavingNotNull(){ assertNotNull(mRoundAmountSaving); }
Run test!!!
加入RoundAmountSaving上的UI元件,測試這些元件的layout或是功能性,
預設情況在38~42行會出現 id cannot be resolved or is not a field 的錯誤,Eclipse 並不會自動修復,我們必須手動import RoundAmountSaving的R(第11行)
package com.foxx.roundamountsaving.test; import android.test.ActivityInstrumentationTestCase2; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.foxx.roundamountsaving.RoundAmountSaving; import com.foxx.roundamountsaving.R; public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving> { private RoundAmountSaving mRoundAmountSaving; private EditText mInputLend; private EditText mInputRate; private EditText mInputNumber; private Button mSubmit; private TextView mAmount; public RoundAmountSavingTest(){ super(RoundAmountSaving.class); } @Override protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mRoundAmountSaving = getActivity(); initLayoutComponents(); } public void testRoundAmountSavingNotNull(){ assertNotNull(mRoundAmountSaving); } private void initLayoutComponents() { mInputLend = (EditText) mRoundAmountSaving.findViewById(R.id.input_lend); mInputNumber = (EditText) mRoundAmountSaving.findViewById(R.id.input_number); mInputRate = (EditText) mRoundAmountSaving.findViewById(R.id.input_rate); mSubmit = (Button) mRoundAmountSaving.findViewById(R.id.submit); mAmount = (TextView) mRoundAmountSaving.findViewById(R.id.amount); } }
加入UI元件不可為null的測試
public void testLayoutComponentsNotNull() { assertNotNull(mInputLend); assertNotNull(mInputNumber); assertNotNull(mInputRate); assertNotNull(mSubmit); }
Run test~
加入測試UI元件layout的測試,assertEquals的第2個參數必須參考RoundAmountSaving的UI xml的設定
public void testAmountLayoutValid() { final ViewGroup.LayoutParams layoutParams = mAmount.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.WRAP_CONTENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputNumberLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputNumber.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputRateLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputRate.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputLendLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputLend.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testSubmitLayoutValid() { final ViewGroup.LayoutParams layoutParams = mSubmit.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); }
Run test~
為了測試UI元件的功能性,加入requestFocusLayoutComponent和setValueToLayoutComponent2個方法,
requestFocusLayoutComponent可讓UI元件取得焦點,取得焦點後再呼叫setValueToLayoutComponent這個方法可以在edittext中設定數值
private void requestFocusLayoutComponent(final View component) { getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { component.requestFocus(); } }); } private void setValueToLayoutComponent(String value) { getInstrumentation().waitForIdleSync(); getInstrumentation().sendStringSync(value); getInstrumentation().waitForIdleSync(); }
設定數值到各UI元件
private void inputTestValueInInputNumber() { requestFocusLayoutComponent(mInputNumber); setValueToLayoutComponent("20"); } private void inputTestValueInInputRate() { requestFocusLayoutComponent(mInputRate); setValueToLayoutComponent("10"); } private void inputTestValueInInputLend() { requestFocusLayoutComponent(mInputLend); setValueToLayoutComponent("100"); }
最後加入測試計算的正確性,expectedCountAmountResult 為預期的計算結果,先設定數值(inutTestValueInInputXXX)到各UI元件中
再呼叫TouchUtils.clickView模擬mSubmit被按下,最後從mAmount取得計算結果和expectedCountAmountResult比較是否相等
相等的話代表算法正確
public void testCountAmount() { final double expectedCountAmountResult = 118.05448347663095; inputTestValueInInputLend(); inputTestValueInInputRate(); inputTestValueInInputNumber(); TouchUtils.clickView(this, mSubmit); String result = (String) mAmount.getText(); assertEquals(result, "" + expectedCountAmountResult); }
Run test~
其實在測試算法還可以加入更多的測試條件,如邊界值,特殊值等等
final RoundAmountSavingTest.java
package com.foxx.roundamountsaving.test; import android.test.ActivityInstrumentationTestCase2; import android.test.TouchUtils; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import com.foxx.roundamountsaving.RoundAmountSaving; import com.foxx.roundamountsaving.R; public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving> { private RoundAmountSaving mRoundAmountSaving; private EditText mInputLend; private EditText mInputRate; private EditText mInputNumber; private Button mSubmit; private TextView mAmount; public RoundAmountSavingTest(){ super(RoundAmountSaving.class); } @Override protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mRoundAmountSaving = getActivity(); initLayoutComponents(); } public void testRoundAmountSavingNotNull(){ assertNotNull(mRoundAmountSaving); } public void testLayoutComponentsNotNull() { assertNotNull(mInputLend); assertNotNull(mInputNumber); assertNotNull(mInputRate); assertNotNull(mSubmit); } private void initLayoutComponents() { mInputLend = (EditText) mRoundAmountSaving.findViewById(R.id.input_lend); mInputNumber = (EditText) mRoundAmountSaving.findViewById(R.id.input_number); mInputRate = (EditText) mRoundAmountSaving.findViewById(R.id.input_rate); mSubmit = (Button) mRoundAmountSaving.findViewById(R.id.submit); mAmount = (TextView) mRoundAmountSaving.findViewById(R.id.amount); } private void requestFocusLayoutComponent(final View component) { getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { component.requestFocus(); } }); } private void setValueToLayoutComponent(String value) { getInstrumentation().waitForIdleSync(); getInstrumentation().sendStringSync(value); getInstrumentation().waitForIdleSync(); } public void testCountAmount() { final double expectedCountAmountResult = 118.05448347663095; inputTestValueInInputLend(); inputTestValueInInputRate(); inputTestValueInInputNumber(); TouchUtils.clickView(this, mSubmit); String result = (String) mAmount.getText(); assertEquals(result, "" + expectedCountAmountResult); } private void inputTestValueInInputNumber() { requestFocusLayoutComponent(mInputNumber); setValueToLayoutComponent("20"); } private void inputTestValueInInputRate() { requestFocusLayoutComponent(mInputRate); setValueToLayoutComponent("10"); } private void inputTestValueInInputLend() { requestFocusLayoutComponent(mInputLend); setValueToLayoutComponent("100"); } public void testAmountLayoutValid() { final ViewGroup.LayoutParams layoutParams = mAmount.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.WRAP_CONTENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputNumberLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputNumber.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputRateLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputRate.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testInputLendLayoutValid() { final ViewGroup.LayoutParams layoutParams = mInputLend.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } public void testSubmitLayoutValid() { final ViewGroup.LayoutParams layoutParams = mSubmit.getLayoutParams(); assertNotNull(layoutParams); assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT); assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT); } }