我在测试 Android Activity 时需要为接口使用不同的实现类,而不是在生产中运行时。我熟悉使用模拟对象,但问题是 Android 上的 Activity 是由操作系统而不是我实例化的,所以当我获得对 Activity 的引用时,它已经被实例化并且已经使用了实现类。就我而言,我正在谈论的类是 Login 类。对于我的自动化测试,我希望能够控制登录结果。这是我的例子:
class MainActivity extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.main);
LoginMgr aLoginMgr = new LoginMgrImpl();
if (aLoginMgr.authenticated()) {
//do something
} else {
//do something else
}
}
}
public interface LoginMgr {
public boolean authenticated();
}
在图书馆的其他地方,我有一个实现类:
public class LoginMgrImpl implements LoginMgr {
//authenticate
}
在自动化测试期间,我希望能够LoginMgrImpl
用一个测试版本替换生产,该版本将返回我测试所需的布尔值。这是我需要帮助的地方,因为我看不到如何MainActivity
使用不同的LoginMgrImpl
. 如果这很重要,我正在使用 Eclipse。当我调用 getActivity() 时,ActivityInstrumentationTestCase2<MainActivity>
测试类会为我创建。MainActivity
它调用无参数构造函数,所以我没有机会在LoginMgrImpl
那里改变。Eclipse 控制构建,所以我看不到MainActivity
用不同的实现库构建的方法。
你们中的任何一个更有经验的人可以为我指明一个成功自动化测试的方向吗?我不能是唯一一个试图模拟 Activity 对象使用的一些测试类的人,但我花了几个小时试图在论坛中找到解决方案,但没有成功。帮助!
根据大家的反馈,我尝试了各种解决方案,并找到了两个我可以接受的解决方案。
解决方案 1:一个在标记答案中进行了解释,涉及在 Eclipse 中设置一个单独的“项目”,该项目具有指向原始 src、res、assets 文件夹以及AndroidManifest.xml
. 然后,我可以修改项目链接版本的项目属性,以引用支持我的测试的不同库实现。这有效,但感觉很笨拙。
解决方案 2:为我的项目定义一个 Application 子类,该子类保留LoginMgr
. 然后将从 Application 实例中MainActivity
检索。LoginMgr
在测试期间,我可以ActivityUnitTestCase<MainActivity>
用来注入一个引用模拟 LoginMgr 的模拟应用程序。以下是代码片段:
主要应用:(确保AndroidManifest.xml
包括<Application android:name="MainApplication" ...>
)
class MainApplication extends Application {
private static LoginMgr sLoginMgr = new LoginMgrImpl();
public LoginMgr getLoginMgr() {
return sLoginMgr;
}
}
MainActivity 必须修改为:
class MainActivity extends Activity {
public void onCreate(Bundle savedState) {
super.onCreate(savedState);
setContentView(R.layout.main);
// 2 lines added
MainApplication aApplication = (MainApplication)getApplication();
LoginMgr aLoginMgr = aApplication.getLoginMgr();
if (aLoginMgr.authenticated()) {
//do something
} else {
//do something else
}
}
}
单元测试必须是这样的:
public class MainActivityTest extends ActivityUnitTestCase<MainActivity> {
public MainActivityTest() {
super(MainActivity.class);
}
public void testUseAlternateLoginMgr() {
MainApplication aMockApplication = new MainApplication()
{
@Override
public LoginMgr getLoginMgr() {
return new LoginMgr() {
public boolean authenticated() { {
return true;
}
};
}
};
setApplication(aMockApplication);
Intent aDependencyIntent = new Intent("android.intent.action.MAIN");
Activity aMainActivity = startActivity(aDependencyIntent, null, null);
// verify results
}
}
我可能会使用模拟框架而不是手动编码的模拟,但这只是一个说明。
我认为解决方案2是更好的方法。谢谢@yorkw!