Android測(cè)試總結(jié)
[TOC]
簡(jiǎn)介
最近整理了Android測(cè)試方便的只是焰轻,主要涉及代碼測(cè)試和自動(dòng)化測(cè)試便瑟。
代碼測(cè)試
Junit
Mockito
Mockwebserver
Android自動(dòng)化測(cè)試
Android monkey
Android monkeyrunner
Android UIAutomator
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缩抡;