I am writing unit test for my viewmodel when I run test I am getting following exception
lateinit property apiInterface has not been initialized
kotlin.UninitializedPropertyAccessException: lateinit property apiInterface has not been initialized
at com.yodgorbek.newstask.presentation.viewmodel.BBCNewsViewModelTest.setUp(BBCNewsViewModelTest.kt:30)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:567)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.RunBefores.invokeMethod(RunBefores.java:33)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.interna
below my BBCNewsViewModelTest.kt where I am getting an exception
@ExperimentalCoroutinesApi
class BBCNewsViewModelTest{
private var progressObserver = MutableLiveData(false)
private lateinit var apiInterface : NewsInterface
private lateinit var newsRepository: NewsRepository
private lateinit var bbcNewsResponseUseCase: BBCNewsResponseUseCase
private lateinit var viewModel: BBCNewsViewModel
@ExperimentalCoroutinesApi
@get:Rule
var mainCoroutineRule = MainCoroutineRule()
@Before
fun setUp() {
newsRepository = NewsRepository(apiInterface)
bbcNewsResponseUseCase = BBCNewsResponseUseCase(newsRepository)
viewModel = BBCNewsViewModel(bbcNewsResponseUseCase)
}
@Test
fun testsSaveSessionData() = runBlockingTest {
assert(progressObserver.value == viewModel.progress.value)
}
}
below my Interface class where I am calling get news function
interface NewsInterface {
@GET(Constants.BBC_URL)
suspend fun getNews(): NewsResponse
}
below BBCNewsViewModel.kt
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.yodgorbek.newstask.model.NewsResponse
import com.yodgorbek.newstask.domain.use_case.BBCNewsResponseUseCase
import com.yodgorbek.newstask.domain.utils.fold
import com.yodgorbek.newstask.domain.utils.parseDate
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.util.Comparator
@RequiresApi(Build.VERSION_CODES.N)
class BBCNewsViewModel(private val useCase: BBCNewsResponseUseCase) : ViewModel() {
var news= MutableLiveData<NewsResponse>()
// Expose to the outside world
// Expose to the outside world
private val error = MutableLiveData<String>()
var progress = MutableLiveData(false)
init{
getNews()
}
@RequiresApi(Build.VERSION_CODES.N)
private fun getNews() {
progress.postValue(true)
viewModelScope.launch(Dispatchers.IO) {
useCase.invoke()
.fold({ newsResponse ->
newsResponse.articles.sortedWith(Comparator.comparing{
it.publishedAt.parseDate()
})
news.postValue(newsResponse)
}, {
error.postValue(it.message)
})
progress.postValue(false)
}
}
}
below my NewsRepository.kt
class NewsRepository(
private val apiInterface:NewsInterface
){
@RequiresApi(Build.VERSION_CODES.N)
suspend fun getNews() : Result<NewsResponse>{
return try {
val response = apiInterface.getNews()
Result.Success(response)
} catch (ex: Exception) {
Result.Error(ex)
}
}
}
below my NewsResponse.kt
data class NewsResponse(
@SerializedName("articles")
val articles: List<Article>,
@SerializedName("status")
val status: String,
@SerializedName("totalResults")
val totalResults: Int
)
I want to know where exactly I am making mistake
CodePudding user response:
According to the exception, you are not initializing the apiInterface
.
In normal cases Retrofit
is responsible for creating the instance of NewsInterface
. But in case of testing, you will be responsible for passing the instance. For that, you can create the mock implementation.
class MockNewsInterface: NewsInterface{
suspend fun getNews(): NewsResponse{
return NewsResponse() // Pass dummy implementation
}
}
Just replace this in your code
newsRepository = NewsRepository(MockNewsInterface())
I would suggest to use Mockito or Mockk.io library. This will help write the test cases easily and efficiently.
CodePudding user response:
As your exception says lateinit property apiInterface has not been initialized
the apiInterface
in BBCNewsViewModelTest
is not initialized
and in your setUp()
method, you're initializing newsRepository = NewsRepository(apiInterface)
while the apiInterface
is not initialized
you can initialize your apiInterface
while declaring without lateinit, or in the setup()
or if your using any Dependency Injection you can do field injection
since you're using retrofit this is how you'd have to initialize
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
NewsInterface service = retrofit.create(NewsInterface.class);