1. 程式人生 > >Android自動化測試框架Robotium

Android自動化測試框架Robotium

目前用於Android的測試框架已經有很多,比如:Monkey、Appium和Robotium等。Monkey這是大家比較熟悉的,Android自帶的系統工具。Monkey通過向系統傳送偽隨機的使用者事件流(如按鍵輸入、觸控式螢幕輸入、手勢輸入等),實現對正在開發的應用程式進行壓力測試,是用於測試軟體的穩定性、健壯性的快速有效的方法。.Appium測試相當於黑盒測試。這個測試框架,一般用於測試UI邏輯的正確性,不能升入測試業務邏輯流程。

Robotium是一款面向Android端的開源自動化測試框架,Robotium結合Android自身提供的測試框架可以對應用程式進行自動化測試。另外,Robotium還支援對WebView的操作。Robotium的核心類是Solo,通過Solo可以對控制元件進行各種操作。

環境配置

  1. 開發工具使用AndroidStudio,AndroidStudio的Junit版本是4.12,可以支援引數化測試。

  2. 在androidTest包下測試程式碼,AndroidStudio重新劃分了工程的結構,分為androidTest、main、test三個資料夾,其中androidTest為Instrmentation測試包,main為原始碼包,test為單元測試包。Robotium是基於Instrmentation的測試框架,所以我們在這個包內寫測試程式碼。

  3. 修改build.gradle

defaultConfig {
    applicationId cfg.package
minSdkVersion cfg.minSdk targetSdkVersion cfg.targetSdk versionCode cfg.version_code versionName cfg.version_name testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } dependencies { compile fileTree(include: ['*.jar'], dir: 'libs') androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2'
, { exclude group: 'com.android.support', module: 'support-annotations' }) androidTestCompile 'com.android.support.test:rules:0.4.1' compile 'com.jayway.android.robotium:robotium-solo:5.6.0' }

注:注意這裡testInstrumentationRunner使用的是android.support.test.runner.AndroidJUnitRunner而不是android.test.InstrumentationTestRunner.

編寫case程式碼

  • 通過@RunWith實現

通過使用@RunWith(AndroidJUnit4.class)註解不需要再繼承ActivityInstrumentationTestCase2類,如果需要引數化執行可修改為@RunWith(Parameterized.class)。很明顯這種方式是基於Junit4實現的。

setUp()做一些啟動測試前的準備工作,如建立Solo例項,啟動Activity等。

@Before
public void setUp() throws Exception {
    solo = new Solo(InstrumentationRegistry.getInstrumentation(),
            activityTestRule.getActivity());
}

tearDown()中測試做一些善後的工作,如結束Activity等

@After
public void tearDown() throws Exception {
    solo.finishOpenedActivities();
}

上述工作準備好之後,剩下的就是我們的測試主體方法了。為了方便主體方法一般以test開頭。

@Test
public void testLogin() throws Exception {

    AutoCompleteTextView name = (AutoCompleteTextView) solo.getView("atv_scarid");
    EditText pwd = (EditText) solo.getView("et_pwd");
    solo.enterText(name, "cnsldg");
    solo.enterText(pwd, "666888");
    solo.clickOnView(solo.getView("bt_login"));
}

整體結構如下:

@RunWith(AndroidJUnit4.class)
public class LoginAcitityTest {


    @Rule
    public ActivityTestRule<LoginActivity> activityTestRule =
            new ActivityTestRule<>(LoginActivity.class);

    private Solo solo;

    @Before
    public void setUp() throws Exception {
        solo = new Solo(InstrumentationRegistry.getInstrumentation(),
                activityTestRule.getActivity());
    }

    @After
    public void tearDown() throws Exception {
        solo.finishOpenedActivities();
    }

    @Test
    public void testLogin() throws Exception {
        AutoCompleteTextView name = (AutoCompleteTextView) solo.getView("atv_scarid");
        EditText pwd = (EditText) solo.getView("et_pwd");
        solo.enterText(name, ""); //清空
        solo.enterText(pwd, ""); //清空
        solo.enterText(name, "cnsldg");
        solo.enterText(pwd, "666888");
        solo.clickOnView(solo.getView("bt_login"));
        boolean result = solo.waitForActivity(HistoryActivity.class, 1000);
        Assert.assertEquals(true, result);
    }
}

在測試方法上右鍵點選執行
測試結果

  • 通過繼承ActivityInstrumentationTestCase2類實現,這種方式基於Junit3比較老,已經被標註為@Deprecated不建議使用。
public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
    private Solo solo;

    public MainActivityTest() {
        super(MainActivity.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        solo = new Solo(getInstrumentation(), getActivity());
    }

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

    public void testAdd()
    {
        EditText et_num1 = (EditText) solo.getView("et_num1");
        EditText et_num2 = (EditText) solo.getView("et_num2");

        solo.enterText(et_num1, "1");
        solo.enterText(et_num2, "2");

        Button btn = (Button) solo.getView("btn");
        TextView tv_sum = (TextView) solo.getView("tv_sum");

        solo.clickOnView(btn);
        //等待點選事件生效
        solo.sleep(200);
        assertEquals("3", tv_sum.getText().toString());
    }
}

注意:測試程式碼並不是在主執行緒執行的,所以我們不能直接通過獲取View來更改UI

Solo類

  • 獲取控制元件
getView(int id)
getView(int id, int index) //index表示控制元件是介面上第幾個相同控制元件
getView(String id) //xml中定義的id屬性值
  • 驗證TextView文字
TextView textView = (TextView)solo.getView("hello");
assertEquals("hello world", textView.getText().toString());
  • 輸入文字
//先獲取控制元件,
enterText(EditText editText, String text)
//index表示頁面上的第幾個輸入框
enterText(int index, String text)
  • 等待對話方塊關閉和開啟
waitForDialogToClose()
waitForDialogToClose(long timeout)
waitForDialogToOpen()
waitForDialogToOpen(long timeout)

long timeout:設定超時時間,單位為毫秒

  • 驗證Activity的載入
//等待指定Activity的出現
waitForActivity(String)
waitForActivity(String, int)
waitForActivity(Class<? extends Activity)
waitForActivity(Class<? extends Activity, int)

String:Activity名稱
int:超時時間,預設為20000,單位為毫秒
Class

//休眠指定時間
sleep(int time)
//從當前介面搜尋指定的文字
searchText(String text)
//等待指定Log的出現
waitForLogMessage(String logMessage)
//等待控制元件的出現
waitForView(int id)
//等待文字的出現
waitForText(String text)
//等待某種載入條件的達成
boolean waitForCondition (Condition condition, int timeout)
  • 滾動滑動操作
//滑動到頂部
scrollTop()
//向上向下滾動螢幕
scrollUp()/scorllDown()
//滾動至ListView第line行
scrollListToLine(AbsListView absListView, int line)
//從其實x,y左邊滑動至終點x,y座標
drag(float fromX, float toX, float fromY, from toY)
  • 其他操作
//截圖,name為圖片的引數,預設路徑是/sdcard/Robotium-Screenshots/
takeScreenshot()
takeScreenshot(String name)
//擷取某段時間內一個序列
takeScreenshotSequence(String name)
//關閉當前已開啟的所有Activity
finishOpenedActivities()
//點選返回鍵
goBack()
//不斷點選返回鍵直至返回到指定Activity
goBackToActivity(String name)
// 收起鍵盤
hideSoftKeyboard()
//設定Activity轉屏方向
setActivityOrientation(int orientation)