来自 新葡8455编程 2020-01-11 18:45 的文章
当前位置: 新葡棋牌京官网app > 新葡8455编程 > 正文

翻译官方文档-自动化UI测试

给测试命名

测试的名字至关重要,特别是从文档角度来看的话。我们应该能够大声读出测试的名字就像一组需求一样。事实上,有一个伟大的IntelliJ插件,叫Enso,它会将你的测试名转变为恰好位于每个类旁边的语句,这样你就可以明明白白地看到你在做什么。

不要以“test”开始命名测试的名称。这是来自于JUnit初期的后遗症,当需要它执行的时候。你的Test类将在Test文件夹中,在一个最后有Test这个单词的类中。会有一个@Test的注解。我们知道这是一个测试。

你也应该避免以“should”或“will”开头。这些都是干扰词。既然你已经为这个功能写了一个测试,那我们就知道它“should或will”工作(如果不能工作的话,那我们知道我们需要修复它)。

图片 1

将测试名称当作一个要求。 下面是一些例子

addingNumbersWillSumValuesTogether()
explodesOnNegativeID()
notifiesListenersOnUpdates()

不要害怕表达出来。如果你的测试名称确实需要很长的一串单词,那就这么做,只要它能清楚说明将发生什么事情。

4.2.创建UI Automator测试类

UI Automator测试类应该写成JUnit 4测试类。要了解更多关于创建JUnit 4测试类和使用JUnit 4断言及注解,请参阅创建设备单元测试类。测试类定义之前,添加 @RunWith(AndroidJUnit4.class)注解,还需要指定安卓测试支持库提供的AndroidJUnitRunner类作为默认测试运行器。在UI Automator测试类中实现下面的开发步骤:

  • 通过调用getInstance()方法并传入Instrumentation对象作为参数,获取UiDevice对象来访问想要测试的设备。
  • 通过调用findObject()方法,获取UiObject对象来访问设备上显示的UI组件(例如,前景中当前视图控件)。
  • 通过调用UiObject的方法,对UI组件模拟特定用户交互;例如,调用performMultiPointerGesture()方法模拟多点触摸手势和setText()方法编辑文本字段。必要时可以反复调用步骤2和3中的APIs,来测试更复杂的用户交互,涉及多个UI组件或用户操作序列。
  • 这些用户交互执行后,检查UI的反应是否是预期的状态或行为。

在后面的几节中将包含更详细的介绍。

那么“好的测试”到底是什么样子的呢?

6.总结


UI测试由于是模拟用户操作,所以属于黑盒测试范畴。一般都是请专业测试人员,设计测试用例来覆盖各种情况,并手动操作设备来完成用例。由于手动操作属于重复、无技术含量的体力工作,可以通过代码来实现自动化,解放测试人员的劳动力,让他们专注于设计更全更好的测试用例。

  • 塑造系统的设计。我们知道输入和输出应该是什么样的,但是我们需要创建什么对象来做到这一点呢?代码应该塑造成什么样的“形状”?编写测试可以让我们知道应该创建什么样的代码。
  • 为了确保初始和持续的正确性。让我们的应用程序如期望地那样运作并且始终如一地精确很重要。测试应该竭力确保做到这一点。
  • 文档。测试是系统的文档,因为它会说明它应该做什么以及应该怎么做。

2.仅单个应用程序的UI测试


测试单个应用程序内的用户交互,有助于确保用户不会遇到意外的结果或与应用程序交互时体验不佳。如果需要验证应用程序的UI是否正常运行,应该养成创建用户界面(UI)测试的习惯。

安卓测试支持库中的Espresso测试框架,为编写UI测试来模拟单个目标应用程序的用户交互提供APIs,可以运行于Android 2.3.3(API 10)及以上版本的设备。使用它的关键好处是,能够自动同步被测试应用程序UI的测试行为,即发现主线程空闲时,能够在适当的时候运行测试命令,提高测试的可靠性。同时,也使开发不用添加任务执行时机相关的代码,比如Thread.sleep()。Espresso测试框架是基于设备的API,且与AndroidJUnitRunner测试运行器一起使用。

本文由码农网 – 小峰原创翻译,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划!

官方文档链接:https://developer.android.google.cn/training/testing/ui-testing/index.html

图片 2

5.UI Automator使用详解


测试代码

测试将分为3个部分:设置,操作,断言。

设置

对你的测试设置代码应该只与在测试中被断言的值相关。如果你有多余的设置代码,那就会搞不清楚它是什么,并且与测试不相关。

这可以通过多种方式实现:

  • 将通用设置移动到使用@Before注解的具体设置方法。
  • 将重复的设置代码移动到辅助方法
  • 使用Maker来创建复杂的测试对象,并只设置测试中相关的值。

我重申一下:每个测试的设置部分应该只有与最后被断言的值相关的代码。

不好的例子:

@Test
    public void returnsBooksWherePartialTitleMatchesInAnyCast(){
        Bookstore bookstore = new Bookstore();
        Book harryPotterOne = new Book("Harry Potter and The Philosopher Stone");
        bookstore.add(harryPotterOne);
        bookstore.add(new Book("Guardians of the Galaxy"));
        Book harryPotterTwo = new Book("The Truth about HARRY POTTER");
        bookstore.add(harryPotterTwo);
        List<Book> results = bookstore.findByTitle("RY pot");
        assertThat(results.size(), is(2));
        assertThat(results, containsInAnyOrder(harryPotterOne, harryPotterTwo));
    }

书店的初始化发生在测试中,书本的创建也是。这让测试显得混乱不堪,让人搞不清楚发生了什么事情。

好的例子:

private Bookstore bookstore = new Bookstore();
private Book aHarryPotterBook = new Book("Harry Potter and The Philosopher Stone");
private Book anotherHarryPotterBook = new Book("The Truth about HARRY POTTER");
private Book aBook = new Book("Guardians of the Galaxy");
@Test
public void returnsBooksWherePartialTitleMatchesInAnyCast(){
    bookstore.add(aHarryPotterBook);
    bookstore.add(aBook);
    bookstore.add(anotherHarryPotterBook);
    List<Book> results = bookstore.findByTitle("RY pot");
    assertThat(results.size(), is(2));
    assertThat(results, containsInAnyOrder(aHarryPotterBook, anotherHarryPotterBook));
}

初始化发生在字段中,这样在测试中发生了什么一清二楚。

操作

小菜一碟!最好保持到一行,你要进行测试的独立操作。有时候,你专门测试的是输出是什么,如果某些东西被多次调用,或者在某些优先操作之后调用的结果是什么,所以这不是一个硬性规定。当读取测试时,用户应该快速而轻松地能说“将这些值设置成这样,如果我执行这个操作/这些操作,那么这是预期的结果”。在上面的例子中,便是bookstore.findByTitle()方法。

断言

使用Hamcrest。 Hamcrest是一个很棒的库,给我们一个流畅的API用来写入测试。不会像这样的代码:

assertEquals(results.size(), 2);
assertTrue(results.contains(aHarryPotterBook))
assertTrue(results.contains(anotherHarryPotterBook))

我们可以一目了然、轻松地阅读像这样的代码:

assertThat(results.size(), is(2));
assertThat(results, containsInAnyOrder(aHarryPotterBook, anotherHarryPotterBook));

这些相当简单的例子:Hamcrest有很多伟大的方法,使编写复杂测试变得很容易,并允许你创建自己的匹配器。

当然,理想情况下,我们希望有一个独立的断言。这可以让我们知道我们正在测试什么,并说明我们的代码没有意外情况。就像这篇文章中所说的那样,这不是一个硬性的规则,因为在某些情况下,这是必要的,但如果你有这样一个的测试:

assertThat(orderBook.bids.size(), is(4));
assertThat(orderBook.asks.size(), is(3));
assertThat(orderBook.bids.get(0).price, is(5200));
assertThat(orderBook.asks.get(2).price, is(10000000));
assertThat(orderBook.asks.get(2).isBuy, is(false));

那么要理解测试哪里失败或哪条断言重要就变得困难多了。

你也可以在Hamcrest中编写自定义的匹配器,因为Hamcrest可为复杂断言提供一个优雅的解决方案。如果你需要在一个循环中运行断言,或者你有大量的字段要断言,那么一个自定义的匹配器可能才是上上之选。

一个测试的最重要的部分之一是,当它失败时,哪怕是一个5岁孩子也应该看得出什么地方出了错以及哪里错了。失败的消息一定不能含糊。关于这方面的解决方法是:

  • 如果做任何类型的对象比较,那么保证对象有一个体面的toString()消息。没有什么比<MyObject @ 142131>不匹配更糟的了。
  • 想要做的更好的话,可以对你的对象使用自定义匹配器。你可以准确地知道哪些字段未能匹配。
  • 确保明确为什么你要选择和这个值作比较。例如,如果你正在将一个字段值与数字3000比较,那么为什么是3000?你应该费力地明白这一点。显然,这个数字不是随便得来的,并且还要确保该变量的命名可以显示它的值是如何得来的。

所有这些都应该是在一个适度的常识范围内。没有严格规定!

你还有什么要补充的吗?欢迎告诉我们。

2.2.创建Espresso测试类

若想创建Espresso测试,需按照下面开发模式编写Java类:

  • 通过调用onView()方法找到Activity中想要测试的UI组件(例如,应用程序的登录按钮),或者onData()方法获取AdapterView的子项。
  • 通过调用ViewInteraction.perform()DataInteraction.perform()方法,模拟作用于UI组件的特定用户交互和传递用户行为(例如,点击登录按钮)。若对同一个UI组件连续执行多个操作,使用逗号分隔的方式将它们列在方法参数中。
  • 必要时重复上述步骤,来模拟用户在目标应用程序上跨界面操作。
  • 使用ViewAssertions的方法来检查,当这些用户交互执行后,UI响应是否符合预期状态或行为。

在后面的几节中将包含更详细的介绍,此处先提供代码片段展示测试类如何调用这个基本工作流:

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

作为测试驱动设计和开发的忠实粉丝,我相信创造良好的测试是我们作为Java开发人员可以做的最重要的事情之一。我们写测试出于许多原因:

3.7.验证结果

调用ViewInteraction.check()或者DataInteraction.check()方法来断言,UI中的视图控件与某些期望的状态相匹配。它们都必须传入一个ViewAssertion对象作为参数,如果断言失败,Espresso抛出AssertionFailedError异常。

ViewAssertions类为特定的常见断言提供一系列帮助方法,可以使用的包括:

  • doesNotExist:断言当前的视图层次结构中,没有视图控件匹配指定的标准。
  • matches:断言指定的视图控件存在于当前视图层次结构中,且它的状态匹配某些指定的Hamcrest匹配器。
  • selectedDescendentsMatch:断言父视图控件中的某些指定子视图控件存在,且它们的状态匹配某些指定的Hamcrest匹配器。

下面的代码片段描述如何检查UI中展示的文本,与之前输入EditText控件的文本一致。

public void testChangeText_sameActivity() {
    // Type text and then press the button.
    ...

    // Check that the text was changed.
    onView(withId(R.id.textToBeChanged))
            .check(matches(withText(STRING_TO_BE_TYPED)));
}
5.1.访问UI组件

UiDevice对象是访问和操作设备状态的主要途径。测试时,可以调用UiDevice的方法来检查各种属性的状态,例如当前横竖屏或显示尺寸。也可以使用UiDevice对象执行设备级别的操作,例如强制设备横竖屏、按方向物理键和按主页及菜单键。

从设备的主屏幕开始(或设备上选定的其它开始位置)测试是好的做法,可以调用UI Automator API提供方法来选择特定UI元素,并与之交互。下面的代码片段展示,测试中如何获取UiDevice实例和模拟按主页键:

import org.junit.Before;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.Until;
...

@RunWith(AndroidJUnit4.class)
@SdkSuppress(minSdkVersion = 18)
public class ChangeTextBehaviorTest {

    private static final String BASIC_SAMPLE_PACKAGE
            = "com.example.android.testing.uiautomator.BasicSample";
    private static final int LAUNCH_TIMEOUT = 5000;
    private static final String STRING_TO_BE_TYPED = "UiAutomator";
    private UiDevice mDevice;

    @Before
    public void startMainActivityFromHomeScreen() {
        // Initialize UiDevice instance
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

        // Start from the home screen
        mDevice.pressHome();

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

        // Launch the app
        Context context = InstrumentationRegistry.getContext();
        final Intent intent = context.getPackageManager()
                .getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
        // Clear out any previous instances
        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);
    }
}

在这个例子中,@SdkSuppress(minSdkVersion = 18)注解有助于确保测试,如UI Automator框架要求的,将只运行在Android 4.3(API 18)及以上版本设备。

使用findObject()方法可获取UiObject,它代表符合给定选择标准的视图控件。若需要,在为同一应用程序创建的其它测试中,可以重复使用该UiObject实例。需注意的是,每次使用UiObject实例来点击UI元素或查询属性,UI Automator测试框架都会搜索当前显示进行匹配。下面的代码片段展示,测试中如何构建代表取消按钮和同意按钮的UiObject实例。

UiObject cancelButton = mDevice.findObject(new UiSelector()
        .text("Cancel")
        .className("android.widget.Button"));
UiObject okButton = mDevice.findObject(new UiSelector()
        .text("OK")
        .className("android.widget.Button"));

// Simulate a user-click on the OK button, if found.
if(okButton.exists() && okButton.isEnabled()) {
    okButton.click();
}
3.1.访问UI组件

在Espresso与被测试应用程序交互之前,必须先指定UI组件或视图控件。Espresso也支持给应用中的特定视图控件和适配器使用Hamcrest匹配器。

为了找到视图控件,调用onView()方法,并传入目标视图特有的匹配器,更详细的描述请参阅指定视图匹配。onView()方法返回允许与视图控件测试交互的ViewInteraction对象,但想要定位RecyclerView布局中的视图控件,调用此方法可能不起作用。因此,可以按照定位AdapterView中视图的指示操作。

注意:Espresso的onView()方法不检查指定的视图是否有效,而是使用提供的匹配器仅搜索当前视图层级。如果没有发现匹配的,此方法会抛出NoMatchingViewException异常。

下面的代码片段展示如何编写测试代码,实现访问EditText控件、输入文本字符串、关闭虚拟键盘和执行按钮点击。

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

    // Check that the text was changed.
    ...
}
5.3.执行交互

一旦测试已经获取UiObject对象,可以调用其中的方法来对自己代表的UI组件执行用户交互,例如指定这样的操作:

  • click():点击UI元素可见范围的中心。
  • dragTo():拖动这个对象到任意位置。
  • setText():在清除可编辑控件的内容之后,设置新的文本。相反的,clearTextField()方法清除可编辑控件内的现有文本。
  • swipeUp():对UiObject执行上滑操作。相似的,swipeDown()swipeLeft()swipeRight()方法会执行对应操作。

UI Automator测试框架不需要使用Shell命令发送Intent或启动Activity,而是通过getContext()方法获取Context对象。下面的代码片段展示,测试中如何使用Intent启动被测试的应用程序。当只对测试计算器应用程序感兴趣,而不关心启动器时,这个方法才有用。

public void setUp() {
    ...

    // Launch a simple calculator app
    Context context = getInstrumentation().getContext();
    Intent intent = context.getPackageManager()
            .getLaunchIntentForPackage(CALC_PACKAGE);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
            // Clear out any previous instances
    context.startActivity(intent);
    mDevice.wait(Until.hasObject(By.pkg(CALC_PACKAGE).depth(0)), TIMEOUT);
}
2.1.设置Espresso

使用Espresso构建UI测试之前,确保配置好测试源代码的位置和项目依赖。在安卓app模块的build.gradle文件中,必须设置对Espresso库的依赖引用:

dependencies {
    // Other dependencies ...
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

关闭测试设备的动画:任由测试设备上的系统动画处于打开状态,可能会导致意外的结果或测试失败。通过打开开发者选项,在设置中关闭动画及以下所有选项:

  • 窗口动画缩放
  • 过渡动画缩放
  • 动画时长缩放

除了Espresso提供的核心API外,还想让项目使用到更多的特性,请参阅资源。

4.1.设置UI Automator

使用UI Automator构建UI测试之前,确保配置好测试源代码的位置和项目依赖。在安卓app模块的build.gradle文件中,必须设置对UI Automator库的依赖引用:

dependencies {
    ...
    androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.1'
}

为了优化UI Automator测试,首先应该检查目标应用程序的UI组件,并确保它们可被访问,具体优化细节将在后面描述。

  • 检查设备上的UI

    设计测试之前,检查UI组件在设备上是否可见,来确保测试时能访问这些组件,从而判断它们是否有可见的文本标签或android:contentDescription属性的值,或以上两者。uiautomatorviewer工具提供方便的可视化界面来检查布局的层次结构和查看设备前景中可见的UI组件的属性。通过这些信息,可使用UI Automator创建更细粒度的测试。例如,可创建匹配特定可见属性的UI选择器。

    启动uiautomatorviewer工具的步骤:

    • 在实际设备上启动目标应用程序。

    • 将设备与开发机器相连接。

    • 打开命令窗口,然后前往<android-sdk>/tools/目录。

    • 使用这个命令运行工具:

      $ uiautomatorviewer
      

    查看应用程序的UI属性:

    • uiautomatorviewer界面,点击Device Screenshot按钮。
    • 通过左面板中显示的快照可以看到由工具识别的UI组件,同时在右下面板中列出属性,在右上面板中展示布局层次结构。
    • 也可以点击Toggle NAF Nodes按钮来查看UI Automator不能访问的UI组件,但能提供的有效信息很有限。

    要了解安卓提供的常见UI组件类型,请参阅用户界面。

  • 确保Activity可被访问

    UI Automator测试框架更适合已实现安卓访问功能的应用程序。若使用View类型的UI元素,或SDK、支持库提供的View的子类,不需要自己实现访问功能,因为这些类已经完成了。但有些应用程序使用自定义UI元素提供更丰富的用户体验,这些元素不会自动提供访问功能。如果应用程序包含View子类(不来自SDK或支持库)的实例,通过完成以下步骤,确保给它们添加访问功能:

    • 创建一个继承自ExploreByTouchHelper的具体类。
    • 通过调用setAccessibilityDelegate()方法,将上面类的实例与指定自定义UI元素关联。

    给自定义视图元素添加访问功能的更多指南,请参阅构建可访问的自定义视图控件。想了解更多关于安卓可访问性的一般最佳实践,请参阅让应用程序更易访问。

5.4.对集合执行交互

如果想要对元素的集合(例如,音乐专辑中的歌曲或收件箱中的邮件)模拟用户交互,可以使用UiCollection类。为了创建UiCollection对象,指定一个UiSelector来搜索UI容器或其它子UI元素的包装器,例如包含子UI元素的布局视图控件。下面的代码片段展示,测试中如何构造UiCollection来代表显示在FrameLayout中的视频专辑:

UiCollection videos = new UiCollection(new UiSelector()
        .className("android.widget.FrameLayout"));

// Retrieve the number of videos in this collection:
int count = videos.getChildCount(new UiSelector()
        .className("android.widget.LinearLayout"));

// Find a specific video and simulate a user-click on it
UiObject video = videos.getChildByText(new UiSelector()
        .className("android.widget.LinearLayout"), "Cute Baby Laughing");
video.click();

// Simulate selecting a checkbox that is associated with the video
UiObject checkBox = video.getChild(new UiSelector()
        .className("android.widget.Checkbox"));
if(!checkBox.isSelected()) checkbox.click();
2.3.配合ActivityTestRule

本节描述如何创建新的符合JUnit 4样式的Espresso测试,同时使用ActivityTestRule减少需要写的样板代码的数量。ActivityTestRule可以让测试框架在每个被@Test注解的测试方法和任何@Before注解的方法调用之前,启动被测试的Activity。还能让框架在测试结束后,处理Activity资源释放,并运行所有@After注解的方法。

package com.example.android.testing.espresso.BasicSample;

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

import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
...

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

    private String mStringToBetyped;

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

    @Before
    public void initValidString() {
        // Specify a valid string.
        mStringToBetyped = "Espresso";
    }

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

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

本文由新葡棋牌京官网app发布于新葡8455编程,转载请注明出处:翻译官方文档-自动化UI测试

关键词: