我们正在构建一个复杂的Android应用程序,其中包含许多活动中分布的许多屏幕和工作流程。我们的工作流程类似于您在银行的ATM机上看到的工作流程,例如,有一个Activity
登录,转换到主菜单Activity
,可以转换到基于用户的其他活动选择。
由于我们有如此多的工作流程,我们需要创建跨多个活动的自动化测试,以便我们可以从头到尾测试工作流程。例如,使用ATM示例,我们要输入有效的PIN,验证是否将我们发送到主菜单,选择提取现金,验证我们是否在提取现金屏幕等等,最终找到自己回到主菜单或“登出”。
我们已经玩过Android附带的测试API(例如ActivityInstrumentationTestCase2
)和Positron,但似乎都无法超出单个Activity
的范围进行测试,虽然我们可以在这些工具中找到一些用于某些单元测试的实用程序,但它们无法满足我们测试跨多个活动的测试场景的需求。
我们对xUnit框架,脚本,GUI录制器/播放等持开放态度,并欢迎任何建议。
答案 0 :(得分:64)
我对回答我自己的赏金问题感到有些尴尬,但这里是......
我对此进行了高调和低调,并且无法相信在任何地方都没有发布任何答案。我已经非常接近了。我现在肯定可以运行跨越活动的测试,但我的实现似乎有一些时间问题,其中测试并不总是可靠地通过。这是我所知道的成功跨多个活动的唯一示例。希望我的提取和匿名化不会引入错误。这是一个简单的测试,我在登录活动中键入用户名和密码,然后在不同的“欢迎”活动中显示正确的欢迎消息:
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个活动的深度,并且我很满意这种方法虽然如我所说,但似乎偶尔出现时间问题,我还没有完全弄明白。我仍然有兴趣听取其他各种活动的测试方法。
答案 1 :(得分:21)
看看 Robotium
'一个开源测试框架,旨在使Android应用程序的自动黑盒测试比开箱即用的Android测试测试更快更容易。
主页:
http://www.robotium.org/
资源:
http://github.com/jayway/robotium
请注意,Robotium项目由我工作的公司维护
答案 2 :(得分:8)
你总是可以使用Robotium。它支持黑盒测试,就像Selenium一样,但支持Android。您可以在Robotium.org找到它
答案 3 :(得分:4)
我很惊讶没有人提到一些领先的自动功能测试工具。与Robotium相比,这些不需要编写Java代码。
MonkeyTalk :由Gorilla Logic公司支持的开源工具。优点:为非技术用户提供更容易录制和更高级别的脚本语言,并且是跨平台的(包括iOS)。鉴于这些优势作为要求,我们发现这是最佳解决方案。它还允许customization超出使用Javascript在其脚本语言中可以完成的任务。
Calabash-Android:一个用于Cucumber风格功能的开源工具。优点:使用Gherkin语言编写功能,即Business Readable,Domain Specific Language,可让您描述软件的行为,而无需详细说明该行为的实现方式。 cucumber-ios中的iOS提供了类似但不完全支持的功能。录制功能不如它们产生二进制输出那么好。
其他几个参考文献:
答案 4 :(得分:3)
我为Android创建了一个记录和播放工具,并在GitHub上提供了该工具。它易于配置和使用,无需编程,可以针对真实设备(无需植根)运行,并在播放测试时自动保存屏幕截图。
答案 5 :(得分:3)
首先,使用'ActivityInstrumentationTestCase2'而不是'InstrumentationTestCase'作为基类。我使用Robotium并定期测试多个活动。我发现我必须将登录活动指定为泛型类型(以及构造函数的类参数)。
'ActivityInstrumentationTestCase2'构造函数忽略package参数,不需要它。不推荐使用该包的构造函数。
来自Javadocs: “ActivityInstrumentationTestCase2(String pkg,Class activityClass) 不推荐使用此构造函数。使用ActivityInstrumentationTestCase2(Class)代替“
使用推荐的基类允许框架处理某些样板,例如启动您的活动。这是通过调用'getActivity()'来完成的,如果有必要的话。
答案 6 :(得分:3)
通过一些修改发现这个很有用。
首先getInstrumentation().waitForIdleSync()
将解决SingleShot所说的脆弱性
此外,InstrumentationTestCase
还有一个lauchActivity
函数,可以替换起始活动行。
答案 7 :(得分: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));
答案 8 :(得分:1)
我正在做同样的事情,我可能会对这个问题的接受答案做出改变,但我确实遇到了 Calculuon ({{在搜索解决方案期间,我会发现<} em 。
答案 9 :(得分:0)
我没有亲自使用它,但ApplicationTestCase看起来可能就是你想要的。
答案 10 :(得分:0)
还有另一种方法可以使用ActivityInstrumentation类进行多项活动。 这是一个正常的自动化场景 首先得到你想要的对象的焦点,然后发送一个密钥 就那么简单 示例代码
button.requestFocus();
sendKeys(KeyEvent.KEYCODE_ENTER);
唯一的理解是每个API调用都会对我们有所帮助。
答案 11 :(得分:0)
接受的方法是否适用于不同应用程序的不同活动,由不同的证书签名?如果没有,Robotium是测试同一应用程序内活动的最佳方式。
答案 12 :(得分: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;
}
答案 13 :(得分:-2)
尝试使用Monkey工具测试
第1步:
打开android studio终端(工具 - >打开终端)
第2步:
要使用monkey,请打开命令提示符,然后只需导航到以下目录。
export PATH=$PATH:/home/adt-bundle-linux-x86-20140702/sdk/platform-tools
第3步:
将此Monkey命令添加到终端并按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