Android 如何对活动启动/发送的意图进行单元测试?
如何创建Android JUnit测试用例来测试活动中生成的意图的内容 我有一个包含EditText窗口的活动,当用户输入完所需的数据后,该活动将启动一个Intent到IntentService,该IntentService记录数据并继续应用程序进程。下面是我要测试的类,OnEditorActionListener/PasscodeEditorListener作为一个单独的类创建:Android 如何对活动启动/发送的意图进行单元测试?,android,junit,android-testing,Android,Junit,Android Testing,如何创建Android JUnit测试用例来测试活动中生成的意图的内容 我有一个包含EditText窗口的活动,当用户输入完所需的数据后,该活动将启动一个Intent到IntentService,该IntentService记录数据并继续应用程序进程。下面是我要测试的类,OnEditorActionListener/PasscodeEditorListener作为一个单独的类创建: public class PasscodeActivity extends BaseActivity {
public class PasscodeActivity extends BaseActivity {
EditText m_textEntry = null;
PasscodeEditorListener m_passcodeEditorListener = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.passcode_activity);
m_passcodeEditorListener = new PasscodeEditorListener();
m_textEntry = (EditText) findViewById(R.id.passcode_activity_edit_text);
m_textEntry.setTag(this);
m_textEntry.setOnEditorActionListener(m_passcodeEditorListener);
}
@Override
protected void onPause() {
super.onPause();
/*
* If we're covered for any reason during the passcode entry,
* exit the activity AND the application...
*/
Intent finishApp = new Intent(this, CoreService.class);
finishApp.setAction(AppConstants.INTENT_ACTION_ACTIVITY_REQUESTS_SERVICE_STOP);
startService(finishApp);
finish();
}
}
class PasscodeEditorListener implements OnEditorActionListener{
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
PasscodeActivity activity = (PasscodeActivity) v.getTag();
boolean imeSaysGo = ((actionId & EditorInfo.IME_ACTION_DONE)!=0)?true:false;
boolean keycodeSaysGo = ((null != event) &&
(KeyEvent.ACTION_DOWN == event.getAction()) &&
(event.getKeyCode() == KeyEvent.KEYCODE_ENTER))?true:false;
if (imeSaysGo || keycodeSaysGo){
CharSequence seq = v.getText();
Intent guidEntry = new Intent(activity, CoreService.class);
guidEntry.setAction(AppConstants.INTENT_ACTION_PASSCODE_INPUT);
guidEntry.putExtra(AppConstants.EXTRA_KEY_GUID, seq.toString());
activity.startService(guidEntry);
return true;
}
return false;
}
}
如何截获活动生成的两个可能的出站意图并验证其内容
谢谢我想到了如何在另一个网站的帮助下使用ContextWrapper 使用ContextWrapper并重写所有的意图函数。为了推广我的所有活动测试,我扩展了ActivityUnitTestCase类,并将解决方案实现为一个垫片。享受:
import android.app.Activity;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.test.ActivityUnitTestCase;
public class IntentCatchingActivityUnitTestCase<T extends Activity> extends ActivityUnitTestCase<T> {
protected Activity m_activity;
protected Instrumentation m_inst;
protected Intent[] m_caughtIntents;
protected IntentCatchingContext m_contextWrapper;
protected class IntentCatchingContext extends ContextWrapper {
public IntentCatchingContext(Context base) {
super(base);
}
@Override
public ComponentName startService(Intent service) {
m_caughtIntents = new Intent[] { service };
return service.getComponent();
}
@Override
public void startActivities(Intent[] intents) {
m_caughtIntents = intents;
super.startActivities(intents);
}
@Override
public void startActivity(Intent intent) {
m_caughtIntents = new Intent[] { intent };
super.startActivity(intent);
}
@Override
public boolean stopService(Intent intent) {
m_caughtIntents = new Intent[] { intent };
return super.stopService(intent);
}
}
// --//
public IntentCatchingActivityUnitTestCase(Class<T> activityClass) {
super(activityClass);
}
protected void setUp() throws Exception {
super.setUp();
m_contextWrapper = new IntentCatchingContext(getInstrumentation().getTargetContext());
setActivityContext(m_contextWrapper);
startActivity(new Intent(), null, null);
m_inst = getInstrumentation();
m_activity = getActivity();
}
protected void tearDown() throws Exception {
super.tearDown();
}
}
导入android.app.Activity;
导入android.app.Instrumentation;
导入android.content.ComponentName;
导入android.content.Context;
导入android.content.ContextWrapper;
导入android.content.Intent;
导入android.test.ActivityUnitTestCase;
公共类IntentCatchingActivityUnitTestCase扩展了ActivityUnitTestCase{
受保护活动m_活动;
受保护仪器仪表;
受保护的意图[]m_caughtIntents;
受保护的IntentCachingContext m_contextWrapper;
受保护的类IntentCachingContext扩展了ContextWrapper{
公共意图TChingContext(上下文库){
超级(基地);
}
@凌驾
公共组件名称startService(意向服务){
m_caughtIntents=新意图[]{service};
return service.getComponent();
}
@凌驾
公共无效性(意图[]意图){
m_caughtIntents=意图;
超级战术(意图);
}
@凌驾
公共空间启动活动(意向){
m_caughtIntents=新意图[]{Intent};
超级触觉(意图);
}
@凌驾
公共布尔停止服务(意图){
m_caughtIntents=新意图[]{Intent};
返回super.stopService(意图);
}
}
// --//
公共意图TChingActivityUnitTestCase(类activityClass){
超级(活动类);
}
受保护的void setUp()引发异常{
super.setUp();
m_contextWrapper=new IntentCatchingContext(getInstrumentation().getTargetContext());
setActivityContext(m_contextWrapper);
startActivity(新意图(),null,null);
m_inst=getInstrumentation();
m_activity=getActivity();
}
受保护的void tearDown()引发异常{
super.tearDown();
}
}
或者,您可以重新考虑您的代码,以便进行“干净”的单元测试(我指的是一个单元测试,除了被测试的类之外,所有内容都被模拟出来)。实际上,我自己也遇到过这样的情况,我得到了一个java.lang.RuntimeException:Stub因为我想要进行单元测试的代码创建了包含我注入的模拟的新意图
我考虑创建我自己的工厂作为意图。然后我可以将模拟工厂注入到我的测试类中:
public class MyClassToBeTested {
public MyClassToBeTested(IntentFactory intentFactory) {
//assign intentFactory to field
}
....
public void myMethodToTestUsingIntents() {
Intent i = intentFactory.create();
i.setAction(AppConstants.INTENT_ACTION_PASSCODE_INPUT);
//when doing unit test, inject a mocked version of the
//IntentFactory and do the necessary verification afterwards.
....
}
}
我的情况和你的不一样,但我相信你也可以应用工厂模式来解决它。我更喜欢编写代码来支持真正的单元测试,但必须承认您的解决方案非常聪明。您使用的是模拟器吗?也许我遗漏了一些东西,但你能不能就这样测试一下?我一直在使用模拟器和手机,虽然我不认为应该有什么不同。我已经看到了许多将意图注入测试中任何特定活动的方法,但没有很多观察输出的方法。他们设置了ContextWrapper并截获了对“startService()”的调用。这适用于第一次调用,但不适用于后续调用。一个活动可以在不关闭的情况下启动多个意图,我感兴趣的是观察/测试它们。这是一个很好的解决方案,不幸的是,它只适用于ActivityUnitTestCase
,而不适用于功能测试用例。是的,我同意。我还发现捕获多个意图也是不可能的,例如,我的活动可能会在某些启动条件下向IntentService发送一个意图,然后在用户按下按钮时启动另一个意图。不可能一网打尽。