获取NullPointerException以在单元测试期间在ViewModel中订阅Observable

时间:2019-03-21 13:40:27

标签: android kotlin mockito rx-java viewmodel

我试图在ViewModel类上运行单元测试,但是当我运行测试时,我得到了NullPointerException。 getDataManager()。getAuthToken()方法使用改型调用API并返回字符串响应。有什么方法可以测试方法“ startLogin()”吗?这是代码。

LoginViewModelTest.kt

@RunWith(JUnit4::class)
class LoginViewModelTest {
    @Rule
    @JvmField
    var instantTaskExecutorRule =  InstantTaskExecutorRule()

    companion object {
        @ClassRule
        @JvmField
        val schedulers = RxSchedulerRule()
    }
    private val application = mock(Application::class.java)
    private val dataManager = mock(DataManager::class.java)
    private val serviceConnector = mock(ServiceConnector::class.java)
    private val requestInterceptor = mock(RequestInterceptor::class.java)

    private lateinit var compositeDisposable: CompositeDisposable
    private lateinit var loginViewModel: LoginViewModel

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        compositeDisposable = CompositeDisposable()
        loginViewModel = LoginViewModel(application, dataManager, serviceConnector, compositeDisposable, requestInterceptor)
    }

    @Test
    fun testLoginWithValidQR(){

        val map = QueryMapBuilder.getAuthTokenHeaders()
        val body = TokenReqBody()
        val success = TokenResSuccess()

        `when`(dataManager.getAuthToken(map, body)).thenReturn(Observable.just(success))

        assertNotNull(dataManager.getAuthToken(map, body))

        // got error here
        loginViewModel.startLogin()

    }
}

LoginViewModel.java

public class LoginViewModel extends BaseViewModel {
    private RequestInterceptor mRequestInterceptor;
    private SingleLiveEvent<LoginViewDataEvents> loginViewDataSingleLiveEvent;

    @Inject
    public LoginViewModel(Application application, DataManager dataManager, ServiceConnector serviceConnector, CompositeDisposable compositeDisposable, RequestInterceptor requestInterceptor) {
        super(application, dataManager, serviceConnector, compositeDisposable);
        mRequestInterceptor = requestInterceptor;
        loginViewDataSingleLiveEvent = new SingleLiveEvent<>();
    }

    public void startLogin() {
        Map<String, String> map = QueryMapBuilder.getAuthTokenHeaders();
        TokenReqBody body = QueryMapBuilder.getAuthTokenBody(AppConfig.getConfig());
        getCompositeDisposable().add(getDataManager().getAuthToken(map, body)
                .subscribeOn(Schedulers.io()) //getting error here
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe()
            );
    }

}

错误日志

java.lang.NullPointerException
at com.example.doc.ui.login.LoginViewModel.startLogin(LoginViewModel.java:102)
at com.example.doc.login.LoginViewModelTest.testLoginWithValidQR(LoginViewModelTest.kt:86)
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)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at com.example.doc.RxSchedulerRule$apply$1.evaluate(RxSchedulerRule.kt:44)

2 个答案:

答案 0 :(得分:0)

startLogin函数中获取null的原因是因为java通过引用传递了非原始变量,这意味着它传递了内存地址而不是值本身。 首次使用dataManager.getAuthToken(map, body)是因为您使用的是定义map语句的相同对象body'when'(它们具有相同的内存地址)。 第二次(在函数内部),您正在使用具有新内存地址的全新对象,因此不会触发'when'语句。 一种解决方案是将'when'语句更改为:

`when`(dataManager.getAuthToken(any(), any())).thenReturn(Observable.just(success))

这意味着该语句将通过传递给函数的 any 对象触发。

答案 1 :(得分:0)

问题是,在单元测试中,您正在为DataManager创建一个新对象

private val dataManager = mock(DataManager::class.java) 

并使用它来调用dataManager.getAuthToken(map, body);

但是,在LoginViewModel内部的实际实现中,您使用以下行,

getDataManager().getAuthToken(map, body)

此处getDataManager()为null。因此,您需要添加以下行使其生效,

when(getDataManager()).thenReturn(dataManager);