如何跨多个 Activity 测试 Android 应用程序?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1759626/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:36:29  来源:igfitidea点击:

How do you test an Android application across multiple Activities?

androidautomated-testsintegration-testingandroid-testing

提问by SingleShot

We are building a complex Android application consisting of many screens and workflows spread across many Activities. Our workflows are similar to what you might see on a Bank's ATM machine, for example, there is an Activityto login in that transitions to a main menu Activitywhich can transition to other activities based on the user's choices.

我们正在构建一个复杂的 Android 应用程序,它由分布在许多活动中的许多屏幕和工作流组成。我们的工作流程类似于您可能在银行的 ATM 机上看到的内容,例如,有一个Activity登录转换到主菜单Activity,该菜单可以根据用户的选择转换到其他活动。

Since we have so many workflows we need to create automated tests that span multiple activities so we can test a workflow from end to end. For example, using the ATM example, we would want to enter a valid PIN, verify that sends us to the main menu, choose withdraw cash, verify that we are on the withdraw cash screen, etc., etc., and eventually find ourselves back on the main menu or "logged" out.

由于我们有如此多的工作流,我们需要创建跨越多个活动的自动化测试,以便我们可以端到端地测试工作流。例如,使用 ATM 示例,我们希望输入有效的 PIN,验证将我们发送到主菜单,选择提取现金,验证我们是否在提取现金屏幕上,等等,最终找到我们自己回到主菜单或“注销”。

We've toyed with the test APIs that come with Android (e.g. ActivityInstrumentationTestCase2) and also with Positron, but neither seem capable of testing beyond the bounds of a single Activity, and while we can find some utility in these tools for some unit testing, they won't meet our needs for testing scenarios that cut across multiple Activities.

我们已经尝试过 Android(例如ActivityInstrumentationTestCase2)和Positron附带的测试 API ,但它们似乎都无法进行超出单个Activity.不能满足我们对跨越多个活动的测试场景的需求。

We are open to an xUnit framework, scripting, GUI recorders/playbacks, etc. and would appreciate any advice.

我们对 xUnit 框架、脚本、GUI 记录器/回放等持开放态度,并希望得到任何建议。

采纳答案by SingleShot

I feel a bit awkward about answering my own bounty question, but here it is...

我对回答我自己的赏金问题感到有点尴尬,但这里是......

I've searched high and low on this and can't believe there is no answer published anywhere. I have come very close. I can definitely run tests that span activities now, but my implementation seems to have some timing issues where the tests don't always pass reliably. This is the only example that I know of that tests across multiple activities successfully. Hopefully my extraction and anonymizing of it did not introduce errors. This is a simplistic test where I type a username and password into a login activity, and then observe a proper welcome message is shown on a different "welcome" activity:

我对此进行了高低搜索,无法相信任何地方都没有发布任何答案。我已经非常接近了。我现在绝对可以运行跨越活动的测试,但我的实现似乎有一些时间问题,这些测试并不总是可靠地通过。这是我所知道的唯一一个在多个活动中成功进行测试的例子。希望我的提取和匿名化不会引入错误。这是一个简单的测试,我在登录活动中输入用户名和密码,然后观察在不同的“欢迎”活动中显示的正确欢迎消息:

package com.mycompany;

import android.app.*;
import android.content.*;
import android.test.*;
import android.test.suitebuilder.annotation.*;
import android.util.*;
import android.view.*;
import android.widget.*;

import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static com.mycompany.R.id.*;

public class LoginTests extends InstrumentationTestCase {

   @MediumTest
   public void testAValidUserCanLogIn() {

      Instrumentation instrumentation = getInstrumentation();

      // Register we are interested in the authentication activiry...
      Instrumentation.ActivityMonitor monitor = instrumentation.addMonitor(AuthenticateActivity.class.getName(), null, false);

      // Start the authentication activity as the first activity...
      Intent intent = new Intent(Intent.ACTION_MAIN);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.setClassName(instrumentation.getTargetContext(), AuthenticateActivity.class.getName());
      instrumentation.startActivitySync(intent);

      // Wait for it to start...
      Activity currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Type into the username field...
      View currentView = currentActivity.findViewById(username_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyUsername");

      // Type into the password field...
      currentView = currentActivity.findViewById(password_field);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(EditText.class));
      TouchUtils.clickView(this, currentView);
      instrumentation.sendStringSync("MyPassword");

      // Register we are interested in the welcome activity...
      // this has to be done before we do something that will send us to that
      // activity...
      instrumentation.removeMonitor(monitor);
      monitor = instrumentation.addMonitor(WelcomeActivity.class.getName(), null, false);

      // Click the login button...
      currentView = currentActivity.findViewById(login_button;
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(Button.class));
      TouchUtils.clickView(this, currentView);

      // Wait for the welcome page to start...
      currentActivity = getInstrumentation().waitForMonitorWithTimeout(monitor, 5);
      assertThat(currentActivity, is(notNullValue()));

      // Make sure we are logged in...
      currentView = currentActivity.findViewById(welcome_message);
      assertThat(currentView, is(notNullValue()));
      assertThat(currentView, instanceOf(TextView.class));
      assertThat(((TextView)currentView).getText().toString(), is("Welcome, MyUsername!"));
   }
}

This code is obviously not very readable. I have actually extracted it into a simple library with an English-like API so I can just say things like this:

这段代码显然不是很可读。我实际上已经将它提取到一个带有类似英语的 API 的简单库中,所以我可以这样说:

type("myUsername").intoThe(username_field);
click(login_button);

I've tested to a depth of about 4 activities and am satisfied that the approach works though as I said, there appears to be an occasional timing issue I have not completely figured out. I am still interested in hearing of any other ways of testing across activities.

我已经测试了大约 4 个活动的深度,并且对这种方法的工作感到满意,尽管正如我所说,似乎偶尔会出现一个我还没有完全弄清楚的时间问题。我仍然有兴趣听到任何其他跨活动测试的方法。

回答by Jonas S?derstr?m

Take a look at Robotium
'a open-source test framework created to make automatic black-box testing of Android applications significantly faster and easier than what is possible with Android instrumentation tests out-of-the-box.'

看看Robotium,
“一个开源测试框架,旨在使 Android 应用程序的自动黑盒测试比开箱即用的 Android 仪器测试更快、更容易”。

Homepage: http://www.robotium.org/
Source: http://github.com/jayway/robotium

主页: http
: //www.robotium.org/来源:http: //github.com/jayway/robotium

Please note that the Robotium project is maintained by the company I work for

请注意,Robotium 项目由我工作的公司维护

回答by Renas

You could always use Robotium. It supports blackbox testing just like Selenium but for Android. You will find it at Robotium.org

你总是可以使用 Robotium。它像 Selenium 一样支持黑盒测试,但适用于 Android。您可以在 Robotium.org 找到它

回答by John Lehmann

I'm surprised no one has mentioned some of the leading automated functional testing tools. Compared with Robotium, these don't require writing Java code.

我很惊讶没有人提到一些领先的自动化功能测试工具。与 Robotium 相比,这些不需要编写 Java 代码。

MonkeyTalk: an open-source tool backed by the company Gorilla Logic. Pros: provides recording as well as a higher-level scripting language easier for non-technical users, and is cross-platform (includes iOS). Given those benefits as requirements, we've found this to be the best solution. It also allows customizationbeyond what can be done in their scripting language using Javascript.

MonkeyTalk:由 Gorilla Logic 公司支持的开源工具。优点:为非技术用户提供更简单的录制以及更高级的脚本语言,并且是跨平台的(包括 iOS)。考虑到这些好处作为要求,我们发现这是最好的解决方案。它还允许自定义超出使用 Javascript 在其脚本语言中可以完成的内容。

Calabash-Android: an open-source tool for Cucumber-style features. Pros: write features in the Gherkin language which is Business Readable, Domain Specific Language that lets you describe software's behavior without detailing how that behavior is implemented. Similar but not exact support is available for iOS in cucumber-ios. Recording capabilities are not as good, as they produce a binary output.

Calabash-Android:一个用于 Cucumber 风格功能的开源工具。优点:用 Gherkin 语言编写功能,它是业务可读的领域特定语言,可让您描述软件的行为,而无需详细说明该行为是如何实现的。在Cucumber-ios 中为 iOS 提供了类似但不完全的支持。记录能力没有那么好,因为它们产生二进制输出。

A couple of other references:

其他几个参考:

  • Here are some additional comparisonsbetween Robotium, Monkeytalk and Calabash. It mentions TestDroidas another possibility.
  • This blogmentions the above plus NativeDriver and Bot-bot.
  • 以下是Robotium、Monkeytalk 和 Calabash 之间的一些额外比较。它提到了TestDroid作为另一种可能性。
  • 这个博客提到了上面加上NativeDriver和Bot-bot。

回答by Lew Bloch

First of all, use 'ActivityInstrumentationTestCase2', not 'InstrumentationTestCase', as your base class. I use Robotium and routinely test across multiple Activities. I found that I have to specify the login activity as the generic type (and class argument to the constructor).

首先,使用“ActivityInstrumentationTestCase2”而不是“InstrumentationTestCase”作为基类。我使用 Robotium 并定期测试多个活动。我发现我必须将登录活动指定为泛型类型(以及构造函数的类参数)。

The 'ActivityInstrumentationTestCase2' constructor ignores the package argument and does not require it. The constructor that takes the package is deprecated.

'ActivityInstrumentationTestCase2' 构造函数忽略包参数并且不需要它。不推荐使用包的构造函数。

From the Javadocs: "ActivityInstrumentationTestCase2(String pkg, Class activityClass) This constructor is deprecated. use ActivityInstrumentationTestCase2(Class) instead"

来自 Javadocs:“ActivityInstrumentationTestCase2(String pkg, Class activityClass) 此构造函数已弃用。改用 ActivityInstrumentationTestCase2(Class)”

Using the recommended base class allows the framework to handle certain boilerplate, like starting your activity. That's done by the call to 'getActivity()', if necessary.

使用推荐的基类允许框架处理某些样板,比如启动你的活动。如有必要,这是通过调用“getActivity()”来完成的。

回答by typingduck

Found this useful with a couple of modifications. Firstly getInstrumentation().waitForIdleSync()will cure the flakiness SingleShot speaks of and also InstrumentationTestCasehas a lauchActivityfunction that can replace the start activity lines.

通过一些修改发现这很有用。首先getInstrumentation().waitForIdleSync()将治愈片状SingleShot讲的,也InstrumentationTestCase有一个lauchActivity功能,可以代替开始活动线路。

回答by Brian Kyckelhahn

I created a record-and-playback tool for Android and made it available on GitHub. It's easy to configure and use, requires no programming, runs against real devices (which do not have to be rooted) and automatically saves screenshots as it plays tests.

我为 Android 创建了一个记录和回放工具,并在GitHub 上提供了它。它易于配置和使用,无需编程,可在真实设备上运行(无需 root),并在播放测试时自动保存屏幕截图。

回答by j2emanue

you can do it like this to avoid the flake waiting times out of sync :

你可以这样做以避免片状等待时间不同步:

final Button btnLogin = (Button) getActivity().findViewById(R.id.button);
Instrumentation instrumentation = getInstrumentation();

// Register we are interested in the authentication activity...
Instrumentation.ActivityMonitor aMonitor = 
        instrumentation.addMonitor(mynextActivity.class.getName(), null, false);

getInstrumentation().runOnMainSync(new Runnable() {
         public void run() {
             btnLogin.performClick();
         }
     });

getInstrumentation().waitForIdleSync();

//check if we got at least one hit on the new activity
assertTrue(getInstrumentation().checkMonitorHit(aMonitor, 1)); 

回答by Peter Ajtai

I'm working on pretty much the same thing, and I'll probably go with a variation on the accepted answer to this question, but I did come across Calculuon(gitHub)during my searches for a solution.

我正在做几乎相同的事情,我可能会对这个问题的公认答案进行修改,但我在搜索解决方案时确实遇到了Calculuon ( gitHub)

回答by user643154

Will accepted approach work with different Activities from different Applications, signed by different certificates? If not, Robotium the best way to test activities within same application.

接受的方法是否适用于来自不同应用程序、由不同证书签名的不同活动?如果没有,Robotium 是在同一应用程序中测试活动的最佳方式。