Android 单元测试实战—— 基于Powermock的常用方法指南
Posted Alex_MaHao
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 单元测试实战—— 基于Powermock的常用方法指南相关的知识,希望对你有一定的参考价值。
上一篇中,基于调研和分析,决定使用Powermock
完成单元测试的编写。
关于Powermock
的使用方式,网上有很多的文章进行解释,下面仅仅介绍一些在android
上的常用姿势。
随着时间推移,该文章会不断完善。
Mock vs Spy
Powermock提供了mock
和spy
两种方式,对于Activity
的私有方法的调用验证通常需要做方法模拟。mock
和spy
都可以实现,mock
是默认对有方法都模拟。spy
是默认对所有方法都不模拟。
个人建议是使用mock
,因为activity
里面的方法逻辑很多,而对于一个单元测试,我们往往只是测试一个方法,对其它方法都需要mock
。用以验证调用或者模拟方法返回值等。
findViewById
activity
中最不缺的就是控件查找,那么直接调用findViewById()
肯定是会报错的Stub
。那么通常的做法是mock
一个activity
,但是mock
的方法的findViewById()
返回值为null。
举例:验证activity
的onCreate
中是否对View
设置了点击监听。
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launcher);
mSkipView = findViewById(R.id.skip);
mSkipView.setOnClickListener(this);
测试代码:
@PrepareForTest(LauncherActivity.class, Build.VERSION.class)
public class LauncherActivityTest extends PowerMockTest
@Mock
LauncherActivity activity;
@Mock
View mSkipView;
@Test
public void onCreateSdk19() throws Exception
PowerMockito.doCallRealMethod().when(activity, "onCreate", ArgumentMatchers.any());
// 当调用findViewById(R.id.skip)时返回mock的View对象
PowerMockito.doReturn(mSkipView).when(activity).findViewById(R.id.skip);
activity.onCreate(null);
// 是否设置监听
Mockito.verify(mSkipView).setOnClickListener(ArgumentMatchers.any());
Whitebox.setInternalState()
final
和private
字段的赋值。
Whitebox.setInternalState(Build.VERSION.class, "SDK_INT", Integer.valueOf(18));
同理,还有获取方法,省略…
需要添加加@PrepareForTest
PowerMockito.supress()
Activity
的声明周期方法中通常会有super.onCreate()
方法,但是super
方法又不能执行,一旦执行就会报错Stub
。
PowerMockito.suppress(Whitebox.getMethod(AppCompatActivity.class, "onCreate", Bundle.class));
通过上面的方式,在测试Activity.onCreate()
时,父类的AppCompatActivity
的onCreate()
就不会再执行。
PowerMockito.whenNew()
如何验证一个页面跳转呢?
现在常用的方式是通过对于意图的验证,判断是否构造了对应包名的意图。
声明当new Intent()
时,返回mock
的intent
对象。
Intent intent = PowerMockito.mock(Intent.class);
PowerMockito.whenNew(Intent.class).withNoArguments().thenReturn(intent);
PowerMockito.whenNew(Intent.class)
.withParameterTypes(Context.class, Class.class)
.withArguments(ArgumentMatchers.any(Context.class), ArgumentMatchers.any(Class.class))
.thenReturn(intent);
验证
PowerMockito.verifyNew(Intent.class).withArguments(activity, MainActivity.class);
Mockito.verify(intent).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
是否构造了MainActivity
的意图,是否添加了flags
。
PowerMockito.thenAnswer()
当构造一个dialog
时,通常会有如下代码:
private void showExpiredDialog()
if (mAlertDialog == null || !mAlertDialog.isShowing())
mAlertDialog = new AlertDialog.Builder(this)
.setMessage("您的登录状态已经过期,请重新登录")
.setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
//
startActivity(AppHelper.makeMainIntent(LogoutDialogActivity.this));
)
.create();
mAlertDialog.setCancelable(false);
mAlertDialog.show();
如何验证setPositiveButton
的OnClickListener
的回调逻辑中的内容是否正确。
@Test
public void logout() throws Exception
PowerMockito.doCallRealMethod().when(activity, "showExpiredDialog");
// 测试回调逻辑
PowerMockito
.when(builder.setPositiveButton(ArgumentMatchers.anyInt(), ArgumentMatchers.any(DialogInterface.OnClickListener.class)))
.thenAnswer(new Answer()
@Override
public Object answer(InvocationOnMock invocation) throws Throwable
Object[] args = invocation.getArguments();
// 获取到第二个参数对象
DialogInterface.OnClickListener arg = (DialogInterface.OnClickListener) args[1];
// 直接执行回调
arg.onClick(null, 0);
return invocation.getMock();
);
Whitebox.invokeMethod(activity, "showExpiredDialog");
PowerMockito.verifyStatic(AppHelper.class);
AppHelper.makeMainIntent(ArgumentMatchers.any(Context.class));
当执行setPositiveButton()
时,会执行Answer
中的回调,此时会直接运行回调方法,然后在验证对应的方法是否执行。
Whitebox.invokeMethod()
通常一些私有方法需要运行,自己写反射还需要改一些东西。
Whitebox.invokeMethod(activity, "showExpiredDialog");
以上是关于Android 单元测试实战—— 基于Powermock的常用方法指南的主要内容,如果未能解决你的问题,请参考以下文章
Android 单元测试实战—— 基于Cobertra&sonarqube的单元测试覆盖率统计