Search code examples
androidunit-testingcontinuous-integrationmockkbitrise

Unit Tests with Mockk library fails on CI but works everytime in local


Hi I have some unit tests for my multi-modular Android Project and I want to run a CI pipeline with these tests but as the header says some tests that are using mockk to mocking operations are failing on CI server (using Bitrise for this with a Ubuntu 22.04 machine) but every time in any situation works on my local machine (MB M1 Pro). I did a lot of research but neither any LLM out there or any blog couldn't help.

Here is my test class in my :feature_home(which does not have access to repositories that is also why im mocking usecases) module:

@OptIn(ExperimentalCoroutinesApi::class)
@ExtendWith(MainCoroutineExtension::class)
class DetailViewModelTest {
    private lateinit var viewModel: DetailViewModel
    private lateinit var getForecastUseCase: GetForecastUseCase
    private lateinit var savedStateHandle: SavedStateHandle

    @BeforeEach
    fun setUp() {
        getForecastUseCase = mockk(relaxed = true)
        savedStateHandle = SavedStateHandle()
        viewModel = DetailViewModel(getForecastUseCase, savedStateHandle)
    }

    @Test
    fun `given viewModel when initialized then forecastState should be Loading`() = runTest {
        assertThat(viewModel.forecastState.value).isEqualTo(ForecastState.Loading)
    }

    @Test
    fun `given savedStateHandle with null values when fetching forecast then state should remain Loading`() =
        runTest {
            savedStateHandle[DetailViewModel.LATITUDE_ARG] = null
            savedStateHandle[DetailViewModel.LONGITUDE_ARG] = null

            every { getForecastUseCase.execute(any(), any()) } returns flowOf(dummyForecast)

            viewModel.forecastState.test {
                val firstItem = awaitItem()
                assertThat(firstItem).isEqualTo(ForecastState.Loading)
                expectNoEvents()
            }
        }
}

GetForecastUseCase:

class GetForecastUseCase @Inject constructor(
    private val repository: WeatherForecastRepository
) {
    fun execute(
        lat: String,
        long: String
    ): Flow<Forecast> = repository.getLocationForecast(lat, long)
}

(on CI) Here, first test always succeeds but the second one (which contains mocking operations) always fails with exception :

java.lang.NullPointerException: Cannot invoke "com.sevban.data.repository.WeatherForecastRepository.getLocationForecast(String, String)" because "this.repository" is null ...

As you can see its complaining about this.repository being null but the thing is I'm not mocking any repository, I'm only mocking the GetForecastUseCase class that uses a repository as a dependency for making requests to API.

What did I try ?

  • I ran repeated tests thousands of times to see whether they are flakky tests or not but they never failed in my local machine.

  • Also I cleaned my gradle & Android Studio cache a lot of times but they were still succeeding in local, failing online.

Are there any suggestions to try on or should this be related with an issue about Bitrise ? Thanks in advance.


Solution

  • Okay, it is solved by changing the Java version of all my modules from 18 to 17.