前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Android测试入门-1

Android测试入门-1

作者头像
luciozhang
发布2023-04-22 16:23:49
发布2023-04-22 16:23:49
70200
代码可运行
举报
文章被收录于专栏:前端lucio前端lucio
运行总次数:0
代码可运行

单元测试 Unit test

在本地执行的单元测试,不需要运行在物理设备或模拟器上,可以测试一些与Android框架无关的代码。

在*build.gradle (Module:app)*添加JUnit4依赖

在*包名(test)*下创建单元测试类,或者直接在需要创建的类的类名上右键->Go To->Test, Create a new test…

选择需要进行测试的方法,在setUp中创建类。

示例

代码语言:javascript
代码运行次数:0
复制
public class EmailValidator {

    public static boolean isValidEmail(String email) {
        if (null == email || "".equals(email))
            return false;
        Pattern p = Pattern.compile("\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*");
        Matcher m = p.matcher(email);
        return m.matches();
    }

}

单元测试:

代码语言:javascript
代码运行次数:0
复制
public class EmailValidatorTest {
    @Before
    public void setUp() throws Exception {

    }

    @Test
    public void isValidEmail() throws Exception {
        assertEquals(EmailValidator.isValidEmail("name@email.com"), true);
    }

}

Instrumented Unit Tests

运行在设备上的测试,但测试与设备相关或者与Android控件相关的功能时,需要选择Instrumented Unit Tests。

测试程序放在*module-name/src/androidTest/java/*。

添加依赖

需要在build.gradle下添加如下依赖。

代码语言:javascript
代码运行次数:0
复制
dependencies {
    androidTestCompile 'com.android.support:support-annotations:24.0.0'
    androidTestCompile 'com.android.support.test:runner:0.5'
    androidTestCompile 'com.android.support.test:rules:0.5'
    // Optional -- Hamcrest library
    androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
    // Optional -- UI testing with Espresso
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
    // Optional -- UI testing with UI Automator
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}

解决同时引入 support-annotationsespresso-core的冲突。

代码语言:javascript
代码运行次数:0
复制
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
    exclude group: 'com.android.support', module: 'support-annotations'
})

配置AndroidJUnitRunner为默认的instrumentation 运行方式。

代码语言:javascript
代码运行次数:0
复制
android {
    defaultConfig {
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
}

示例

在测试类前面添加注解 @RunWith(AndroidJUnit4.class)

下面是一个对于UI控件的Instrumented 测试。

activity_main.xml

代码语言:javascript
代码运行次数:0
复制
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:text="@string/hello_world" android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <EditText
        android:hint="Enter your name here"
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Say hello!"
        android:layout_below="@+id/editText"
        android:onClick="sayHello"/>
</RelativeLayout>

MainActivity.java

代码语言:javascript
代码运行次数:0
复制
public void sayHello(View v){
    TextView textView = (TextView) findViewById(R.id.textView);
    EditText editText = (EditText) findViewById(R.id.editText);
    textView.setText("Hello, " + editText.getText().toString() + "!");
}

测试程序

代码语言:javascript
代码运行次数:0
复制
package com.example.testing.testingexample;

import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.action.ViewActions;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class MainActivityInstrumentationTest {

    private static final String STRING_TO_BE_TYPED = "Peter";

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

    @Test
    public void sayHello(){
        onView(withId(R.id.editText)).perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard()); //找到id为editText的输入框,键入"Peter",关闭软键盘(实际操作时需要考虑输入法语言)

        onView(withText("Say hello!")).perform(click()); //点击文本为"Say hello!"的按钮

        String expectedText = "Hello, " + STRING_TO_BE_TYPED + "!";
        onView(withId(R.id.textView)).check(matches(withText(expectedText))); //判断id为textView的文本框内容是否与期望符合
    }

}

运行测试可以在虚拟机或物理设备上看到上述的操作,模拟键盘输入时,需要注意因为输入法语言可能带来的不一致。

Show Passed按钮

显示所有方法的测试。

Automating User Interface Tests

Automating User Interface Tests用于测试UI界面,为用户提供高质量的用户界面和稳定的交互。

两种Automating UI Tests:

Testing UI for a Single App :确保交互界面正确。使用espresso框架。

Testing UI for Multiple Apps :测试多终端行为的正确,如不同终端的app之间通信。使用uiautomator框架。

Espresso框架

基于Instrumentation的开源自动化测试框架,规模小、简洁,API精确,编写测试代码简单,容易快速上手。但不能跨app。

为了避免动画可能带来的错误,需要在开发者选项中关闭下面几项动画。

  • Window animation scale
  • Transition animation scale
  • Animator duration scale

添加依赖

代码语言:javascript
代码运行次数:0
复制
dependencies {
    // Other dependencies ...
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

创建Espresso测试的流程

  1. 定位控件,onView()或onData() (后一个用于从定位AdapterView 的item)。
  2. 模拟操作,ViewInteraction.perform()或DataInteraction.perform() ,不定项参数为具体操作的队列,逗号分开。
  3. 重复上述过程,模拟用户在多activity之间的操作。
  4. 验证结果, ViewAssertions的方法如match()验证控件中结果是否正确。
代码语言:javascript
代码运行次数:0
复制
onView(withId(R.id.my_view))            // withId(R.id.my_view) is a ViewMatcher
        .perform(click())               // click() is a ViewAction
        .check(matches(isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

示例

代码语言:javascript
代码运行次数:0
复制
@RunWith(AndroidJUnit4.class)
@LargeTest
public class ChangeTextBehaviorTest {

    public static String STRING_TO_BE_TYPED;

    // @Rule注解来启动一个activity
    @Rule
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(
            MainActivity.class);

    // @Before注解表示在测试前执行
    @Before
    public void initValidString() {
        // Specify a valid string.
        STRING_TO_BE_TYPED = "espresso";
    }

    @Test
    public void changeText_sameActivity() {
        // Type text and then press the button.
        onView(withId(R.id.editTextUserInput))
                .perform(typeText(STRING_TO_BE_TYPED), closeSoftKeyboard());
        onView(withId(R.id.changeTextBt)).perform(click());

        // Check that the text was changed.
        onView(withId(R.id.textToBeChanged)).check(matches(withText(STRING_TO_BE_TYPED)));
    }

    @Test
    public void changeText_newActivity() {
        // Type text and then press the button.
        onView(withId(R.id.editTextUserInput)).perform(typeText(STRING_TO_BE_TYPED),
                closeSoftKeyboard());
        onView(withId(R.id.activityChangeTextBtn)).perform(click());

        // This view is in a different Activity, no need to tell Espresso.
        onView(withId(R.id.show_text_view)).check(matches(withText(STRING_TO_BE_TYPED)));
    }
}

完整代码:https://github.com/googlesamples/android-testing/tree/master/ui/espresso/BasicSample

定位

定位UI控件

onView() 返回一个 ViewInteraction对象用于测试控件,没找到控件会扔出一个NoMatchingViewException。

匹配文字

代码语言:javascript
代码运行次数:0
复制
onView(withText("Sign-in"));

匹配id

代码语言:javascript
代码运行次数:0
复制
onView(withId(R.id.button_signin));

匹配多个条件

代码语言:javascript
代码运行次数:0
复制
onView(allOf(withId(R.id.button_signin), not(withText("Sign-out"))));

定位AdapterView里面的View

onData() 返回一个DataInteraction对象,没找到控件也会扔出一个NoMatchingViewException。

代码语言:javascript
代码运行次数:0
复制
onData(allOf(is(instanceOf(Map.class)),
        hasEntry(equalTo(LongListActivity.ROW_TEXT), is("test input")));

模拟操作

ViewInteraction.perform() 和 DataInteraction.perform()的参数为一个或多个 ViewAction对象。

可以指定的对象:ViewActions.click()、ViewActions.typeText()、ViewActions.scrollTo()、ViewActions.pressKey()、ViewActions.clearText()。

利用Espresso Intents进行测试

用于截断activity或service发出的intent,验证其传递的内容是否正确。

添加依赖
代码语言:javascript
代码运行次数:0
复制
dependencies {
  // Other dependencies ...
  androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'
}
示例
代码语言:javascript
代码运行次数:0
复制
@Large
@RunWith(AndroidJUnit4.class)
public class SimpleIntentTest {

    private static final String MESSAGE = "This is a test";
    private static final String PACKAGE_NAME = "com.example.myfirstapp";

    /* Instantiate an IntentsTestRule object. */
    @Rule
    public IntentsTestRule≶MainActivity> mIntentsRule =
      new IntentsTestRule≶>(MainActivity.class);

    @Test
    public void verifyMessageSentToMessageActivity() {

        // Types a message into a EditText element.
        onView(withId(R.id.edit_message))
                .perform(typeText(MESSAGE), closeSoftKeyboard());

        // Clicks a button to send the message to another
        // activity through an explicit intent.
        onView(withId(R.id.send_message)).perform(click());

        // Verifies that the DisplayMessageActivity received an intent
        // with the correct package name and message.
        intended(allOf(
                hasComponent(hasShortClassName(".DisplayMessageActivity")),
                toPackage(PACKAGE_NAME),
                hasExtra(MainActivity.EXTRA_MESSAGE, MESSAGE)));

    }
}

完整代码:https://github.com/googlesamples/android-testing/tree/master/ui/espresso/IntentsBasicSample

利用Espresso Web测试WebView

用于检测activity里面webview的行为。

添加依赖
代码语言:javascript
代码运行次数:0
复制
dependencies {
  // Other dependencies ...
  androidTestCompile 'com.android.support.test.espresso:espresso-web:2.2.2'
}

需测试的WebView必须在指定activity时设置enable JavaScript的,我们可以选择WebView中的HTML元素并模拟用户操作。

示例
代码语言:javascript
代码运行次数:0
复制
@LargeTest
@RunWith(AndroidJUnit4.class)
public class WebViewActivityTest {

    private static final String MACCHIATO = "Macchiato";
    private static final String DOPPIO = "Doppio";

    @Rule
    public ActivityTestRule mActivityRule =
        new ActivityTestRule(WebViewActivity.class,
            false /* Initial touch mode */, false /*  launch activity */) {

        @Override
        protected void afterActivityLaunched() {
            // Enable JavaScript.
            onWebView().forceJavascriptEnabled();
        }
    }

    @Test
    public void typeTextInInput_clickButton_SubmitsForm() {
       // Lazily launch the Activity with a custom start Intent per test
       mActivityRule.launchActivity(withWebFormIntent());

       // Selects the WebView in your layout.
       // If you have multiple WebViews you can also use a
       // matcher to select a given WebView, onWebView(withId(R.id.web_view)).
       onWebView()
           // Find the input element by ID
           .withElement(findElement(Locator.ID, "text_input"))
           // Clear previous input
           .perform(clearElement())
           // Enter text into the input element
           .perform(DriverAtoms.webKeys(MACCHIATO))
           // Find the submit button
           .withElement(findElement(Locator.ID, "submitBtn"))
           // Simulate a click via JavaScript
           .perform(webClick())
           // Find the response element by ID
           .withElement(findElement(Locator.ID, "response"))
           // Verify that the response page contains the entered text
           .check(webMatches(getText(), containsString(MACCHIATO)));
    }
}

完整代码:https://github.com/googlesamples/android-testing/tree/master/ui/espresso/WebBasicSample

验证结果

ViewInteraction.check() 或 DataInteraction.check() 验证控件是否满足期望的状态。这两个方法必须传一个ViewAssertion对象作为参数,断言失败会扔出一个AssertionFailedError。

可用的断言

doesNotExist:当前等级没有符合标准的view

matches:特定view符合给定标准

selectedDescendentsMatch:父view的特定子view存在且满足给定标准

参考文章

https://io2015codelabs.appspot.com/codelabs/android-studio-testing#1

https://developer.android.com/training/testing/index.html

http://www.jianshu.com/p/74af4c7043c9

google示例代码:https://github.com/googlesamples/android-testing

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 单元测试 Unit test
    • 示例
  • Instrumented Unit Tests
    • 添加依赖
    • 示例
  • Automating User Interface Tests
  • Espresso框架
    • 添加依赖
    • 创建Espresso测试的流程
    • 示例
    • 定位
      • 定位UI控件
      • 匹配文字
      • 匹配id
      • 匹配多个条件
      • 定位AdapterView里面的View
    • 模拟操作
      • 利用Espresso Intents进行测试
      • 利用Espresso Web测试WebView
    • 验证结果
  • 参考文章
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档