Получение NullPointerException для подписки на Observable во ViewModel во время модульного теста - PullRequest
0 голосов
/ 21 марта 2019

Я пытаюсь запустить модульный тест для своего класса 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 голосов
/ 28 марта 2019

Проблема заключается в том, что в модульном тесте вы создаете новый объект для DataManager

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

и используете его для вызова dataManager.getAuthToken(map, body);

Однако в реальной реализации внутри LoginViewModel выиспользуйте следующую строку:

getDataManager().getAuthToken(map, body)

Здесь getDataManager () имеет значение null.Поэтому вам нужно добавить строку ниже, чтобы она работала,

when(getDataManager()).thenReturn(dataManager);
0 голосов
/ 24 марта 2019

Причина получения нулевого значения внутри функции startLogin заключается в том, что java передает не примитивные переменные по ссылке, что означает, что он передает адрес памяти, а не само значение. Первое использование dataManager.getAuthToken(map, body) проходит, потому что вы используете те же объекты map и body, которые определили оператор 'when' (они имеют один и тот же адрес памяти). Во второй раз (внутри функции) вы используете совершенно новые объекты с новым адресом памяти, поэтому оператор 'when' не срабатывает. Решением для этого является изменение оператора 'when' на:

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

Это означает, что оператор будет запущен с любым объектом, переданным функции.

...