java - 使用PowerMock在android中进行单元测试单例类方法

标签 java android mockito rx-java powermock

我需要对Singleton类的方法进行单元测试,该类内部使用RxJava Singles,并使用PowerMock测试框架来模拟静态类和方法。我尝试了各种方法来模拟 Schedulers.io() 和 AndroidSchedulers.mainThread() 方法,但它不起作用。我在 UserApi.verifyUserData() 方法内的 .subscribeOn(Schedulers.io()) 行收到 java.lang.NullPointerException 错误。

Singleton Class UserApi (Class under Test)

final public class UserApi {
    private CompositeDisposable compositeDisposable;
    private String userID; 
    //private final SchedulerProvider schedulerProvider;

    private UserApi(String userId) {
        super();
        this.userID = userId;
        //this.schedulerProvider = schedulerProvider;
    }

    public static UserApi getInstance() {
        return SingletonHolder.sINSTANCE;
    }

    private static final class SingletonHolder {
        private static final UserApi sINSTANCE;

        static {
            String uuid = UUID.randomUUID().toString();
            sINSTANCE = new UserApi(uuid);
        }
    }

    // Rest Api call

    public void verifyUserData(byte[] doc, byte[] img) {
        this.compositeDisposable = new CompositeDisposable();
        String docStr = Base64.encodeToString(doc, Base64.NO_WRAP);
        String imgStr = Base64.encodeToString(img, Base64.NO_WRAP);

        final Disposable apiDisposable = IdvManager.getInstance().getUserManager().verifyUserData(docStr, imgStr)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<JsonObject>() {
                    @Override
                    public void accept(JsonObject verifyResponse) throws Exception {
                        pollResult();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable error) throws Exception {
                      // handle error code...
                    }
                });
        this.compositeDisposable.add(apiDisposable);
    }

    private void pollResult() {
        // code here...
    }

}

UserManager Class and Interface

public interface UserManager {

    Single<JsonObject> verifyUserData(String docStr, String imgStr);

}

final class UserManagerImpl implements UserManager {

    private final UserService userService;

    UserManagerImpl(final Retrofit retrofit) {
        super();
        this.userService = retrofit.create(UserService.class);
    }
    @Override
    public Single<JsonObject> verifyUserData(String docStr, String imgStr) {
     // Code here...
    }
}

Unit Test

@RunWith(PowerMockRunner.class)
@PrepareForTest({IdvManager.class, Base64.class, Schedulers.class, AndroidSchedulers.class, UserApi.class})
public class UserApiTest {

    @Mock
    public UserManager userManager;
    @Mock
    private Handler handler;

    private IdvManager idvManager;
    private Schedulers schedulers;

    private UserApi spyUserApi;
    private TestScheduler testScheduler;
    private String userID;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        testScheduler = new TestScheduler();
        handler = new Handler();
        PowerMockito.suppress(constructor(IdvManager.class));
        // mock static
        PowerMockito.mockStatic(IdvManager.class);
        PowerMockito.mockStatic(Schedulers.class);
        PowerMockito.mockStatic(AndroidSchedulers.class);
        PowerMockito.mockStatic(Base64.class);
        // Create mock for class
        idvManager = PowerMockito.mock(IdvManager.class);
        schedulers = PowerMockito.mock(Schedulers.class);
        PowerMockito.when(IdvManager.getInstance()).thenReturn(IdvManager);
        when(idvManager.getUserManager()).thenReturn(userManager);

        spyUserApi = PowerMockito.spy(UserApi.getInstance());
        // TestSchedulerProvider testSchedulerProvider = new TestSchedulerProvider(testScheduler);

        when(Base64.encodeToString((byte[]) any(), anyInt())).thenAnswer(new Answer<Object>() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                return java.util.Base64.getEncoder().encodeToString((byte[]) invocation.getArguments()[0]);
            }
        });

        when(schedulers.io()).thenReturn(testScheduler);
        when(AndroidSchedulers.mainThread()).thenReturn(testScheduler);
        userID = UUID.randomUUID().toString();
    }

    @After
    public void clearMocks() {
        //Mockito.framework().clearInlineMocks();
    }

    @Test
    public void verifyUserData_callsPollResult_returnsResponse() {
        // Input
        String docStr = "iVBORw0KGgoAAAANSUhEUgAAAJ4AAACeCAYAAADDhbN7AA.....";
        // Output
        JsonObject verifyResponse = new JsonObject();
        verifyResponse.addProperty("status", "Response created");
        doReturn(Single.just(verifyResponse)).when(userManager).verifyUserData(docStr, docStr);
        // spy method call
        spyUserApi.verifyUserData(docFrontArr, docFrontArr);
        testScheduler.triggerActions();
        // assert
        verify(userManager).verifyUserData(docStr, docStr);

    }
}

Error

java.lang.NullPointerException
    at com.rahul.manager.UserApi.verifyUserData(UserApi.java:60)
    at com.rahul.manager.UserApiTest.verifyUserData_callsPollResult_returnsResponse(UserApiTest.java:171)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)

我不确定是否可以通过使用 PowerMock 监视 Singleton 类的真实实例来测试 Singleton 类的方法。

最佳答案

测试代码很复杂,因为它不可测试且不可扩展。它到处都包含硬编码的依赖项(例如用户 ID、处理程序、几个单例)。
如果您决定使用另一种 id 生成方法或其他处理程序,则在不重写整个类的情况下将无法做到这一点。
不要对依赖项进行硬编码,而是在构造函数(对于强制依赖项)或 setter (对于可选依赖项)中请求它们。
这将使您的代码可扩展且可测试。执行此操作后,您将看到您的类包含多个职责,将它们移至单独的类后,您将获得更好的画面:-)

例如:

public UserApi(String userId, Handler handle) {
    this.userId = userId;
    this.handler = handler;
}

关于java - 使用PowerMock在android中进行单元测试单例类方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58935432/

相关文章:

java - 在 Web 应用程序中获取当前 URL

java如何在属性中配置注释调度程序fixedDelay

android - 如何在 Kotlin 中获取 Android 设备 token 以用于 Firebase 云消息传递

java - Mockito 使用参数模拟新实例调用

java - 使用 junit 测试输入

java - Parse.com 使用 java 创建 objectId

android - 如何使用ListView.FixedViewInfo?

android - 案例变体存在吗?

scala - 尝试在 Play 中运行 Mockito 测试时出现错误消息

java - 使用 RMI 传输文件