80

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

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

我们玩弄了 Android(例如ActivityInstrumentationTestCase2)和Positron附带的测试 API ,但似乎都无法测试超出单个 的范围Activity,虽然我们可以在这些工具中找到一些用于某些单元测试的实用程序,但他们赢了不能满足我们对跨越多个活动的测试场景的需求。

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

4

14 回答 14

65

回答我自己的赏金问题我觉得有点尴尬,但在这里......

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

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!"));
   }
}

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

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

我已经测试了大约 4 项活动的深度,并且很满意该方法有效,但正如我所说,似乎偶尔会出现我还没有完全弄清楚的时间问题。我仍然有兴趣听到任何其他跨活动测试的方法。

于 2009-11-28T05:22:26.777 回答
22

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

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

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

于 2010-01-26T21:31:35.993 回答
8

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

于 2010-01-21T20:30:57.940 回答
4

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

MonkeyTalk:由 Gorilla Logic 公司支持的开源工具。优点:为非技术用户提供录制和更高级的脚本语言,并且是跨平台的(包括 iOS)。鉴于这些好处作为要求,我们发现这是最好的解决方案。它还允许自定义超出使用 Javascript 的脚本语言所能完成的功能。

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

其他几个参考:

  • 以下是Robotium、Monkeytalk 和 Calabash 之间的一些额外比较。它提到TestDroid作为另一种可能性。
  • 这篇博客提到了上面加上 NativeDriver 和 Bot-bot。
于 2013-01-28T19:35:27.747 回答
3

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

于 2011-08-18T19:26:09.747 回答
3

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

'ActivityInstrumentationTestCase2' 构造函数忽略包参数并且不需要它。接受包的构造函数已被弃用。

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

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

于 2012-05-31T23:30:34.320 回答
3

通过一些修改发现这很有用。首先getInstrumentation().waitForIdleSync()将治愈SingleShot所说的片状,并且还InstrumentationTestCase具有lauchActivity可以替换启动活动线的功能。

于 2012-07-11T20:52:13.843 回答
2

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

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)); 
于 2012-11-25T17:10:15.427 回答
1

我正在做几乎相同的事情,我可能会对这个问题的公认答案做出改变,但在我寻找解决方案的过程中,我确实遇到了Calculuon ( gitHub ) 。

于 2011-11-22T02:11:23.250 回答
0

我没有亲自使用过它,但 ApplicationTestCase 看起来可能是您正在寻找的东西。

于 2009-11-19T22:31:31.840 回答
0

还有另一种方法可以使用 ActivityInstrumentation 类进行多项活动。它是一个正常的自动化场景...首先获得您想要的任何对象的焦点,然后发送一个键简单的示例代码

button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);

唯一的事情是了解每个 API 调用都会对我们有所帮助。

于 2009-12-08T06:06:36.357 回答
0

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

于 2011-04-06T21:52:56.333 回答
0

该答案基于已接受的答案,但经过修改以解决对我来说在添加大约六个测试后变得一致的时间问题。@pajato1 因解决时间问题而受到赞誉,正如接受的答案评论中所引用的那样。

/**
 * Creates a test Activity for a given fully qualified test class name.
 *
 * @param fullyQualifiedClassName The fully qualified name of test activity class.
 *
 * @return The test activity object or null if it could not be located.
 */
protected AbstractTestActivity getTestActivity(final String fullyQualifiedClassName) {
    AbstractTestActivity result = null;

    // Register our interest in the given activity and start it.
    Log.d(TAG, String.format("Running test (%s) with main class: %s.", getName(), fullyQualifiedClassName));
    instrumentation = getInstrumentation();

    Intent intent = new Intent(Intent.ACTION_MAIN);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setClassName(instrumentation.getTargetContext(), fullyQualifiedClassName);
    // Wait for the activity to finish starting
    Activity activity = instrumentation.startActivitySync(intent);

    // Perform basic sanity checks.
    assertTrue("The activity is null!  Aborting.", activity != null);
    String format = "The test activity is of the wrong type (%s).";
    assertTrue(String.format(format, activity.getClass().getName()), activity.getClass().getName().equals(fullyQualifiedClassName));
    result = (AbstractTestActivity) activity;

    return result;
}
于 2015-03-30T05:01:53.000 回答
0

试试 Monkey 工具测试

第1步:

打开android studio终端(工具->打开终端)

第2步:

为了使用 monkey ,打开命令提示符并导航到以下目录。

 export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools

第 3 步:

将此猴子命令添加到终端并按 enter ..

看看你的模拟器的魔力。

adb shell monkey -p com.example.yourpackage -v 500

500 - 它是频率计数或要发送以进行测试的事件数。

你可以改变这个计数..

更多参考,

http://www.tutorialspoint.com/android/android_testing.htm

http://androidtesting.blogspot.in/2012/04/android-testing-with-monkey-tool.html

于 2015-05-20T14:58:58.063 回答