Home > Software engineering >  lateinit property apiInterface has not been initialized kotlin.UninitializedPropertyAccessException:
lateinit property apiInterface has not been initialized kotlin.UninitializedPropertyAccessException:

Time:02-20

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);
  • Related