官網教學,參考 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);
}
}