我正在尝试在我的ViewModel类上运行单元测试,但是当我运行测试时,我将获得NullPoInterException。getDatamanager()。getAuthToken()方法使用Raturofit调用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)
在 startLogin
函数中获取null的原因是因为java通过参考传递了非主要变量,这意味着它传递了内存地址,而不是值本身。dataManager.getAuthToken(map, body)
的首次使用传递是因为您使用的是定义'when'
语句的相同对象map
和body
(它们具有相同的内存地址)。第二次(在函数内部),您使用的是具有新内存地址的全新对象,因此'when'
语句不会触发。解决方案是将'when'
语句更改为:
`when`(dataManager.getAuthToken(any(), any())).thenReturn(Observable.just(success))
这意味着语句将用>传递给该功能的任何触发。
问题是,在单位测试中,您正在为datamanager创建一个新对象
private val dataManager = mock(DataManager::class.java)
并使用它来调用dataManager.getAuthToken(map, body);
但是,在loginviewModel内部的实际实现中,您使用以下行,
getDataManager().getAuthToken(map, body)
这里getDatamanager()为null。因此,您需要添加以下行使其工作,
when(getDataManager()).thenReturn(dataManager);