Android测试总结

Android测试总结


[TOC]

简介

最近整理了Android测试方便的只是,主要涉及代码测试和自动化测试。

代码测试

Junit

JUnit浅谈

Mockito

Mockito浅谈

Mockwebserver

MockWebServer

Android自动化测试

Android monkey

Android Monkey整理

Android monkeyrunner

Android monkeyrunner整理

Android UIAutomator

Android UIAutomator浅谈

Android Espresso

Android Espresso浅谈

自动化测试示例

下面示例一个Android项目,就是一个简单的登录页面,依次使用上面介绍的自动化测试方案测试界面。

首先是界面布局:

<?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>

其次是页面代码:

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提供的是一个python文件,然后调用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());
    }
}

自动化测试总结

  • Monkey
    准确来说,这不算是自动化测试,因为其只能产生随机的事件,无法按照既定的步骤操作;

  • Monkeyrunner
    优点:操作最为简单,可以录制测试脚本,可视化操作;
    缺点:主要生成坐标的自动化操作,移植性不强,功能最为局限,上面代码中已经显示出来,完全使用的数字坐标,移植到另外一个设备,则不能运行。

  • UiAutomator
    优点:可以对所有操作进行自动化,操作简单;
    缺点:Android版本需要高于4.3,无法根据控件ID操作,相对来说功能较为局限,但也够用了;

  • Espresso
    优点:主要针对某一个APK进行自动化测试,APK可以有源码,也可以没有源码,功能强大;
    缺点:针对APK操作,因此操作相对复杂;

总结:由上面介绍可以有这样的结论:测试某个APK,可以选择Espresso;测试过程可能涉及多个APK,选择UiAutomator;一些简单的测试,选择Monkeyrunner;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容