Android測(cè)試總結(jié)

Android測(cè)試總結(jié)


[TOC]

簡(jiǎn)介

最近整理了Android測(cè)試方便的只是焰轻,主要涉及代碼測(cè)試和自動(dòng)化測(cè)試便瑟。

代碼測(cè)試

Junit

JUnit淺談

Mockito

Mockito淺談

Mockwebserver

MockWebServer

Android自動(dòng)化測(cè)試

Android monkey

Android Monkey整理

Android monkeyrunner

Android monkeyrunner整理

Android UIAutomator

Android UIAutomator淺談

Android Espresso

Android Espresso淺談

自動(dòng)化測(cè)試示例

下面示例一個(gè)Android項(xiàng)目掂为,就是一個(gè)簡(jiǎn)單的登錄頁(yè)面贰健,依次使用上面介紹的自動(dòng)化測(cè)試方案測(cè)試界面贱勃。

首先是界面布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:id="@+id/activity_main"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="20dp"
    tools:context="cn.mycommons.testcase.MainActivity">

    <EditText
        android:id="@+id/edtUserName"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:hint="Input user name" />

    <EditText
        android:id="@+id/edtPasswd"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:hint="Input password"
        android:contentDescription="Input password"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/btnLogin"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="Login" />
</LinearLayout>

其次是頁(yè)面代碼:

package cn.mycommons.testcase;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    EditText edtUserName;
    EditText edtPasswd;
    Button btnLogin;

    String userName;
    String passwd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        edtUserName = (EditText) findViewById(R.id.edtUserName);
        edtPasswd = (EditText) findViewById(R.id.edtPasswd);
        btnLogin = (Button) findViewById(R.id.btnLogin);

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                userName = edtUserName.getText().toString();
                passwd = edtPasswd.getText().toString();
                if (check(userName, passwd)) {
                    doLogin(userName, passwd);
                }
            }
        });
    }

    boolean check(String userName, String passwd) {
        do {
            if (userName.length() < 5) {
                showToast("User name invalidate");
                break;
            }
            if (passwd.length() < 5) {
                showToast("Password invalidate");
                break;
            }
            return true;
        } while (false);
        return false;
    }

    void doLogin(String userName, String passwd) {
        if ("admin".equals(userName) && "admin".equals(passwd)) {
            showToast("Login success");
            startActivity(new Intent(this, SuccessActivity.class));
        } else {
            showToast("Login fail");
        }
    }

    void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}

Monkey

Monkey只是檢查使用下shell命令即可昂勒。

adb shell monkey -p cn.mycommons.testcase -v 50000

Monkey Runner

Monkey Runner提供的是一個(gè)python文件蜀细,然后調(diào)用monkeyrunner命令即可。

$ monkeyrunner test_case.py
# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice

# Connects to the current device, returning a MonkeyDevice object
print 'wait for device connection.'
device = MonkeyRunner.waitForConnection()
print 'connect device success.'

# Takes a screenshot
result = device.takeSnapshot()
print 'takeSnapshot success.'
# Writes the screenshot to a file
result.writeToFile('./test_case1.png','png')
print 'save image to file success.'

# input user name
device.touch(200,380,'DOWN_AND_UP')
print 'touch user name'
for x in xrange(1,100):
    device.press("KEYCODE_DEL",'DOWN_AND_UP')
print 'delete user name'
device.type("admin")
print 'input admin to user name'

# input passwd 
device.touch(200,500,'DOWN_AND_UP')
print 'touch password'
for x in xrange(1,100):
    device.press("KEYCODE_DEL",'DOWN_AND_UP')
print 'delete password'
device.type("admin")
print 'input admin to password'

# press login button
device.touch(200,680,'DOWN_AND_UP')
print 'press login button'

MonkeyRunner.sleep(2)

# Takes a screenshot
result = device.takeSnapshot()
print 'takeSnapshot success.'
# Writes the screenshot to a file
result.writeToFile('./test_case2.png','png')
print 'save image to file success.'

UiAutomator

package cn.mycommons.testcase;

import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.JELLY_BEAN_MR2)
public class MainActivityTest {

    private static final String BASIC_SAMPLE_PACKAGE = "cn.mycommons.testcase";
    private static final int LAUNCH_TIMEOUT = 5000;
    private static final String STRING_TO_BE_TYPED = "UiAutomator";

    private UiDevice mDevice;

    @Before
    public void before() {
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
        mDevice.pressHome();

        // Wait for launcher
        final String launcherPackage = mDevice.getLauncherPackageName();
        assertThat(launcherPackage, Matchers.notNullValue());
        mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);

        Context context = InstrumentationRegistry.getContext();
        final Intent intent = context.getPackageManager().getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        context.startActivity(intent);

        // Wait for the app to appear
        mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT);
    }

    @Test
    public void testLogin1() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();

        edtUserName.setText("admin");
        edtPasswd.setText("admin");
        login.click();

        mDevice.waitForWindowUpdate(BuildConfig.FLAVOR, 3000);
    }

    @Test
    public void testLogin2() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("");
        edtPasswd.setText("");

        login.click();
    }

    @Test
    public void testLogin3() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("abc");
        edtPasswd.setText("");

        login.click();
    }

    @Test
    public void testLogin4() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("");
        edtPasswd.setText("abc");

        login.click();
    }

    @Test
    public void testLogin5() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("abc");
        edtPasswd.setText("abc");

        login.click();
    }

    @Test
    public void testLogin6() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));

        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("abcedf");
        edtPasswd.setText("abc");

        login.click();
    }

    @Test
    public void testLogin7() throws UiObjectNotFoundException {
        UiObject login = mDevice.findObject(new UiSelector().text("Login"));
        UiObject edtUserName = mDevice.findObject(new UiSelector().text("Input user name"));
        UiObject edtPasswd = mDevice.findObject(new UiSelector().descriptionContains("Input password"));


        edtUserName.clearTextField();
        edtPasswd.clearTextField();
        edtUserName.setText("abcedf");
        edtPasswd.setText("abcedf");

        login.click();
    }
}

Espressor

package cn.mycommons.testcase;

import android.support.test.espresso.Espresso;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
public class MainActivityEspressoTest {

    @Rule
    public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(MainActivity.class);

    @Test
    public void testLogin() {
        Espresso.onView(ViewMatchers.withContentDescription(R.id.edtUserName)).perform(ViewActions.typeText("admin"));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("admin"));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }

    @Test
    public void testLogin1() {
        Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText(""));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText(""));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }

    @Test
    public void testLogin2() {
        Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abc"));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText(""));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }

    @Test
    public void testLogin3() {
        Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText(""));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abc"));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }

    @Test
    public void testLogin4() {
        Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abc"));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abc"));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }

    @Test
    public void testLogin5() {
        Espresso.onView(ViewMatchers.withId(R.id.edtUserName)).perform(ViewActions.typeText("abcdef"));
        Espresso.onView(ViewMatchers.withId(R.id.edtPasswd)).perform(ViewActions.typeText("abcdef"));
        Espresso.onView(ViewMatchers.withId(R.id.btnLogin)).perform(ViewActions.click());
    }
}

自動(dòng)化測(cè)試總結(jié)

  • Monkey
    準(zhǔn)確來(lái)說(shuō)戈盈,這不算是自動(dòng)化測(cè)試奠衔,因?yàn)槠渲荒墚a(chǎn)生隨機(jī)的事件,無(wú)法按照既定的步驟操作塘娶;

  • Monkeyrunner
    優(yōu)點(diǎn):操作最為簡(jiǎn)單归斤,可以錄制測(cè)試腳本,可視化操作刁岸;
    缺點(diǎn):主要生成坐標(biāo)的自動(dòng)化操作脏里,移植性不強(qiáng),功能最為局限虹曙,上面代碼中已經(jīng)顯示出來(lái)迫横,完全使用的數(shù)字坐標(biāo),移植到另外一個(gè)設(shè)備酝碳,則不能運(yùn)行矾踱。

  • UiAutomator
    優(yōu)點(diǎn):可以對(duì)所有操作進(jìn)行自動(dòng)化,操作簡(jiǎn)單击敌;
    缺點(diǎn):Android版本需要高于4.3介返,無(wú)法根據(jù)控件ID操作,相對(duì)來(lái)說(shuō)功能較為局限沃斤,但也夠用了圣蝎;

  • Espresso
    優(yōu)點(diǎn):主要針對(duì)某一個(gè)APK進(jìn)行自動(dòng)化測(cè)試,APK可以有源碼衡瓶,也可以沒(méi)有源碼徘公,功能強(qiáng)大;
    缺點(diǎn):針對(duì)APK操作哮针,因此操作相對(duì)復(fù)雜关面;

總結(jié):由上面介紹可以有這樣的結(jié)論:測(cè)試某個(gè)APK坦袍,可以選擇Espresso;測(cè)試過(guò)程可能涉及多個(gè)APK等太,選擇UiAutomator捂齐;一些簡(jiǎn)單的測(cè)試,選擇Monkeyrunner缩抡;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末奠宜,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子瞻想,更是在濱河造成了極大的恐慌压真,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蘑险,死亡現(xiàn)場(chǎng)離奇詭異滴肿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)佃迄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)泼差,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人和屎,你說(shuō)我怎么就攤上這事拴驮。” “怎么了柴信?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵套啤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我随常,道長(zhǎng)潜沦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任绪氛,我火速辦了婚禮唆鸡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枣察。我一直安慰自己争占,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布序目。 她就那樣靜靜地躺著臂痕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猿涨。 梳的紋絲不亂的頭發(fā)上握童,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音叛赚,去河邊找鬼澡绩。 笑死稽揭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肥卡。 我是一名探鬼主播溪掀,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼召调!你這毒婦竟也來(lái)了膨桥?” 一聲冷哼從身側(cè)響起蛮浑,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤唠叛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后沮稚,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體艺沼,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蕴掏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了障般。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡盛杰,死狀恐怖挽荡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情即供,我是刑警寧澤定拟,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站逗嫡,受9級(jí)特大地震影響青自,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驱证,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一延窜、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧抹锄,春花似錦逆瑞、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至车份,卻和暖如春谋减,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背扫沼。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工出爹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留庄吼,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓严就,卻偏偏與公主長(zhǎng)得像总寻,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子梢为,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容