Android程序员

PowerMockito使用方法和避坑指南

2021-01-31  本文已影响0人  Erich_Godsen

常用方法

  1. PowerMockito.spy(T object)可用于mock某个对象,调用该对象某个方法时会根据class调用其真实方法逻辑,

在不加入PrepareForTest的情况下,仅支持non-private & non-final classes类型的class

mSpyAdapter = PowerMockito.spy(new VideoAdapter());
  1. PowerMockito.mock(T object) 模拟某个class,但是不会调用其具体的方法,但是支持打桩

在不加入PrepareForTest的情况下,仅支持non-private & non-final classes类型的class

BaseViewHolder help = PowerMockito.mock(BaseViewHolder.class);
GlideImageView view = PowerMockito.mock(GlideImageView.class);
PowerMockito.doReturn(view).when(helper).getView(R.id.image);
mSpyAdapter.convert(help, item);
  1. PowerMockito.mockStatic(Class<?> type, Class<?>... types),对一个类中所有方法进行静态模拟,一般用于工具类上
PowerMockito.mockStatic(RxBus.class);
  1. 关于抽象类的模拟,注意第二个参数
mSpyAbstractActivity =  Mockito.mock(AbstractActivity.class, Mockito.CALLS_REAL_METHODS);

第一种方式

PowerMockito.field(Class<?> declaringClass, String fieldName)

第二种方式

Whitebox.getField(Class<?> type, String fieldName)

其实上面两种方式殊途同归,最终都是调用第二种方式,如果想要获取就用get,如果想要设置就用set

//获取字段
IMediaPlayer.OnPreparedListener listener = (IMediaPlayer.OnPreparedListener) field(
                IjkVideoView.class, "mPreparedListener").get(mIjkVideoView);

//设置字段
field(IjkVideoView.class, "mVideoHeight").set(mIjkVideoView, 100);
PowerMockito.method(Class<?> declaringClass, String methodName, Class<?>... parameterTypes)

如果要精确查找,可在方法名后面跟上参数列表

//模糊匹配
Method method = method(IjkVideoView.class, "bindSurfaceHolder");
method.invoke(mIjkVideoView, null, null);
//精确查找
method = method(IjkVideoView.class, "bindSurfaceHolder", IMediaPlayer.class, IRenderView.ISurfaceHolder.class);
method.invoke(mIjkVideoView, null, null);
 mVideoPlay.setOnErrorListener(new IMediaPlayer.OnErrorListener() {
            @Override
            public boolean onError(IMediaPlayer mp, int what, int extra) {
                Log.d(TAG, "onError what=" + what + " extra=" +  extra + " feedItem:" + mFeedItem);
                return false;
            }
        });

下面给大家介绍PowerMockito.doAnswer(Answer<?> answer)这个利器

    /**
     * Use doAnswer() when you want to stub a void method with generic
     * {@link Answer}.
     * <p>
     * Stubbing voids requires different approach from
     * {@link Mockito#when(Object)} because the compiler does not like void
     * methods inside brackets...
     * <p>
     * Example:
     * <p>
     * <pre>
     * doAnswer(new Answer() {
     *     public Object answer(InvocationOnMock invocation) {
     *         Object[] args = invocation.getArguments();
     *         Mock mock = invocation.getMock();
     *         return null;
     *     }
     * }).when(mock).someMethod();
     * </pre>
     * <p>
     * See examples in javadoc for {@link Mockito} class
     *
     * @param answer to answer when the stubbed method is called
     * @return stubber - to select a method for stubbing
     */
    public static PowerMockitoStubber doAnswer(Answer<?> answer) {
        return POWERMOCKITO_CORE.doAnswer(answer);
    }

使用方法如下:

 doAnswer((Answer<Void>) invocation -> {
            //拿到设置的接口回调,这个0就是setOnErrorListener这个接口传入的第一个参数
            IMediaPlayer.OnErrorListener listener = invocation.getArgument(0);
            //下面可以针对不同的情况传入不同的测试参数
            listener.onError(null, 0, 0);
            return null;
        }).when(mVideoPlay).setOnErrorListener(any(IMediaPlayer.OnErrorListener.class));

 public SurfaceRenderView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        //getHolder如果不打桩会一直报空指针异常
        getHolder().addCallback(mSurfaceCallback);
        getHolder().setType(SurfaceHolder.SURFACE_TYPE_NORMAL);
    }

如何打桩呢?需要用到PowerMockito.replace(Method method)方法

//首先需要找到'getHolder'这个方法,不要找错了,这个方法在父类中
Method method = PowerMockito.method(SurfaceView.class, "getHolder");
        PowerMockito.replace(method).with((proxy, method1, args) -> {
            //然后返回我们mock的对象
            return PowerMockito.mock(SurfaceHolder.class);
        });
//然后再调用构造器
mRenderView = PowerMockito.spy(new SurfaceRenderView(PowerMockito.mock(Context.class)));
mockStatic(SingleInstance.class);
SingleInstance instance = mock(SingleInstance.class);
when(SingleInstance.getInstance()).thenReturn(instance);

最后不要忘了将 SingleInstance.class加入到PrepareForTest

@RunWith(PowerMockRunner.class)
@PrepareForTest({SingleInstance.class})
public class SingleManagerTest {
        .........
}

最后需要提一下加入到PrepareForTest中到副作用,如果当前测试到类就是SingleInstance.class这个类,那么加入以后,你跑出来的TestResult报告中该类的覆盖率为零,所以要慎重,加入相关的依赖类没有关系。

public class MainActivity extends AppCompatActivity {

        private class NetworkChangeReceiver extends BroadcastReceiver {
 
        @Override
        public void onReceive(Context context, Intent intent) {
                Log.d("action:" + intent.getAction());
        }
    }
}

在外部类MainActivity中含有一个私有内部类NetworkChangeReceiver,此时该如何写单测呢?

    @Test
    public void testNetworkReceiver() throws Exception {
        //找到内部类
        Class clazz = Whitebox.getInnerClassType(MainActivity.class, "NetworkChangeReceiver");
        //获取构造器,因为是非静态内部类(持有父类引用),默认构造器中需要传入父类对象
        Constructor constructor = Whitebox.getConstructor(clazz, MainActivity.class);
        BroadcastReceiver receiver = (BroadcastReceiver) constructor.newInstance(mActivity);
        clazz.getConstructor(MainActivity.class).newInstance(mActivity);
        Intent intent = new Intent();
        //根据各种情况传入测试数据
        receiver.onReceive(context, intent);
    }
上一篇 下一篇

猜你喜欢

热点阅读