Единичное тестирование вещательного приемника?
Здесь BroadcastReceiver из моего проекта, который я ищу unit test. Когда пользователь делает телефонный звонок, он захватывает номер телефона и устанавливает намерение начать новое действие, передавая номер телефона.
public class OutgoingCallReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context xiContext, Intent xiIntent)
{
if (xiIntent.getAction().equalsIgnoreCase(Intent.ACTION_NEW_OUTGOING_CALL))
{
String phoneNum = xiIntent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Intent intent = new Intent(xiContext, MyActivity.class);
intent.putExtra("phoneNum", phoneNum);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
xiContext.startActivity(intent);
setResultData(null);
}
}
}
До сих пор мой unit test выглядит следующим образом:
public class OutgoingCallReceiverTest extends AndroidTestCase
{
private OutgoingCallReceiver mReceiver;
@Override
protected void setUp() throws Exception
{
super.setUp();
mReceiver = new OutgoingCallReceiver();
}
public void testStartActivity()
{
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(getContext(), intent);
}
}
Это выполняется через код, но я хочу, чтобы мой тест мог проверить, было ли отправлено намерение, и проверить номер телефона на нем. Как это сделать?
Можно ли также проверить, что телефонный звонок отменен (из-за строки setResultData (null))?
Ответы
Ответ 1
Матовый,
Похоже, вам нужно макетировать контекст... а затем перевести свои методы на прием интерфейсов вместо конкретных классов: public void onReceive(IContext c, IIntent i)
, только для целей тестирования. Но тогда классы Context и Intent не принадлежат вам, они... они Android... поэтому вы не можете "просто" заставить их реализовать свои интерфейсы, поэтому вам придется "обернуть" их, чтобы выставляйте свой интерфейс, а именно RATHER - много кода для получения большого выигрыша. Очень Yucky!!!
Итак, я начал задаваться вопросом, прошел ли кто-то все это раньше, и сделал для нас твердые дворы... и tada: http://developer.android.com/reference/android/test/mock/package-summary.html
Приветствия. Кит.
Ответ 2
corlettk указал мне на объект MockContext в Android, что делает трюк. Я создал подкласс TestContext, который выглядит следующим образом:
public class TestContext extends MockContext
{
private List<Intent> mReceivedIntents = new ArrayList<Intent>();
@Override
public String getPackageName()
{
return "com.mypackage.test";
}
@Override
public void startActivity(Intent xiIntent)
{
mReceivedIntents.add(xiIntent);
}
public List<Intent> getReceivedIntents()
{
return mReceivedIntents;
}
}
И мой тестовый пример теперь выглядит следующим образом:
public class OutgoingCallReceiverTest extends AndroidTestCase
{
private OutgoingCallReceiver mReceiver;
private TestContext mContext;
@Override
protected void setUp() throws Exception
{
super.setUp();
mReceiver = new OutgoingCallReceiver();
mContext = new TestContext();
}
public void testStartActivity()
{
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(mContext, intent);
assertEquals(1, mContext.getReceivedIntents().size());
assertNull(mReceiver.getResultData());
Intent receivedIntent = mContext.getReceivedIntents().get(0);
assertNull(receivedIntent.getAction());
assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
}
}
Ответ 3
Поскольку этот вопрос задавали, насмешливые Рамочные структуры развивались в значительной степени. С помощью mockito вы можете теперь высмеять не только интерфейсы, но и классы. Поэтому я бы предложил решить эту проблему, издеваясь над контекстом и используя ArgumentCapture:
import static org.mockito.Mockito.*;
public class OutgoingCallReceiverTest extends AndroidTestCase {
private OutgoingCallReceiver mReceiver;
private Context mContext;
@Override
protected void setUp() throws Exception {
super.setUp();
//To make mockito work
System.setProperty("dexmaker.dexcache",
mContext.getCacheDir().toString());
mReceiver = new OutgoingCallReceiver();
mContext = mock(Context.class);
}
public void testStartActivity() {
Intent intent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
intent.putExtra(Intent.EXTRA_PHONE_NUMBER, "01234567890");
mReceiver.onReceive(mContext, intent);
assertNull(mReceiver.getResultData());
ArgumentCaptor<Intent> argument = ArgumentCaptor.forClass(Intent.class);
verify(mContext, times(1)).startActivity(argument.capture());
Intent receivedIntent = argument.getValue();
assertNull(receivedIntent.getAction());
assertEquals("01234567890", receivedIntent.getStringExtra("phoneNum"));
assertTrue((receivedIntent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) != 0);
}
}