Skip to content

Hw test #6

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: hw5-weather-app-hilt-single-live-event
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .idea/detekt.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ dependencies {
def coroutines = "1.6.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines"

// region Network
def retrofit = "2.9.0"
Expand All @@ -104,8 +105,13 @@ dependencies {
implementation "it.czerwinski.android.hilt:hilt-extensions:${hiltExt}"
kapt "it.czerwinski.android.hilt:hilt-processor:${hiltExt}"


testImplementation 'junit:junit:4.13.2'
testImplementation 'io.mockk:mockk:1.12.3'
testRuntimeOnly "org.junit.vintage:junit-vintage-engine:5.8.2"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.2"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.2"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5:1.6.21"

androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,6 @@ class WeatherViewModel @Inject constructor(
private val getWeatherUseCase: GetWeatherUseCase
) : ViewModel() {

// @AssistedFactory
// interface Factory {
// fun create(@Assisted cityId: Int): Factory
// }
//
// @Suppress("UNCHECKED_CAST")
// companion object {
// fun provideFactory(
// assistedFactory: Factory,
// cityId: Int
// ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
// override fun <T : ViewModel> create(modelClass: Class<T>): T {
// return assistedFactory.create(cityId) as T
// }
// }
// }

private var _weather: MutableLiveData<Result<Weather>> = MutableLiveData()
val weather: LiveData<Result<Weather>> get() = _weather

Expand All @@ -50,7 +33,3 @@ class WeatherViewModel @Inject constructor(
}
}
}

//@Module
//@InstallIn(ActivityRetainedComponent::class)
//interface AssistedInjectModule
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ru.itis.karakurik.androidLab2.domain.usecase

import io.mockk.*
import ru.itis.karakurik.androidLab2.utils.MainCoroutineRule
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DisplayName
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(JUnit4::class)
internal class GetWeatherListUseCaseTest {
@MockK
lateinit var repository: WeatherRepository

@get:Rule
val coroutineRule: MainCoroutineRule = MainCoroutineRule()

private lateinit var useCase: GetWeatherListUseCase

@Before
fun setUp() {
MockKAnnotations.init(this)
useCase = spyk(GetWeatherListUseCase(repository, coroutineRule.testDispatcher))
}

@Test
@DisplayName("Successful getting weather list")
operator fun invoke() = runBlocking {
val expectedList = arrayListOf<Weather>(
mockk { every { id } returns 1 },
mockk { every { id } returns 2 },
)

coEvery { repository.getWeatherList(any(), any(), any()) } returns expectedList

val result = useCase(1.0, 1.0, 5)

assertEquals(expectedList, result)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ru.itis.karakurik.androidLab2.domain.usecase

import io.mockk.*
import ru.itis.karakurik.androidLab2.utils.MainCoroutineRule
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DisplayName
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.repository.WeatherRepository

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(JUnit4::class)
internal class GetWeatherUseCaseTest {
@MockK
lateinit var repository: WeatherRepository

@get:Rule
val coroutineRule: MainCoroutineRule = MainCoroutineRule()

private lateinit var useCase: GetWeatherUseCase

@Before
fun setUp() {
MockKAnnotations.init(this)
useCase = spyk(GetWeatherUseCase(repository, coroutineRule.testDispatcher))
}

@Test
@DisplayName("Successful getting weather")
operator fun invoke() = runBlocking {
val expected = mockk<Weather>() {
every { id } returns 1
}

coEvery { repository.getWeather(1) } returns expected

val result = useCase(1)

assertEquals(expected, result)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package ru.itis.karakurik.androidLab2.presentation.fragments.cities

import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherListUseCase
import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
import ru.itis.karakurik.androidLab2.utils.getOrAwaitValue

internal class WeatherViewModelTest {

@MockK
lateinit var getWeatherUseCase: GetWeatherUseCase

@MockK
lateinit var getWeatherListUseCase: GetWeatherListUseCase

private lateinit var viewModel: CityListViewModel

@Before
fun setUp() {
MockKAnnotations.init(this)
viewModel = CityListViewModel(
getWeatherUseCase,
getWeatherListUseCase
)
}

@Test
fun getCityIdByName() = runBlocking {

val mockCityId = mockk<Weather> { every { id } returns 1 }.id
val expectedCityId = Result.success(mockCityId)

coEvery { getWeatherUseCase("Kazan").id } returns mockCityId

viewModel.onGetCityId("Kazan")

assertEquals(expectedCityId, viewModel.cityId.getOrAwaitValue())
}

@Test
fun getWeatherList() = runBlocking {

val mockCityList = arrayListOf<Weather>(
mockk { every { id } returns 1 },
mockk { every { id } returns 2 },
)

val expectedWeather = Result.success(mockCityList)

coEvery { getWeatherListUseCase(any(), any(), any()) } returns mockCityList

viewModel.onGetWeatherList(1.0, 1.0, 5)

assertEquals(expectedWeather, viewModel.weatherList.getOrAwaitValue())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ru.itis.karakurik.androidLab2.presentation.fragments.weather

import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.DisplayName
import ru.itis.karakurik.androidLab2.domain.entity.Weather
import ru.itis.karakurik.androidLab2.domain.usecase.GetWeatherUseCase
import ru.itis.karakurik.androidLab2.utils.getOrAwaitValue

internal class WeatherViewModelTest {

@MockK
lateinit var getWeatherUseCase: GetWeatherUseCase

private lateinit var viewModel: WeatherViewModel

@Before
fun setUp() {
MockKAnnotations.init(this)
viewModel = WeatherViewModel(getWeatherUseCase)
}

@Test
fun getWeatherById() = runBlocking {

val mockWeather = mockk<Weather> { every { id } returns 1 }
val expectedWeather = Result.success(mockWeather)

coEvery { getWeatherUseCase(1) } returns mockWeather

viewModel.onGetWeather(1)

assertEquals(expectedWeather, viewModel.weather.getOrAwaitValue())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ru.itis.karakurik.androidLab2.utils

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description
import kotlin.coroutines.ContinuationInterceptor

@ExperimentalCoroutinesApi
class MainCoroutineRule : TestWatcher(), TestCoroutineScope by TestCoroutineScope() {

val testDispatcher = StandardTestDispatcher()

override fun starting(description: Description) {
super.starting(description)
Dispatchers.setMain(this.coroutineContext[ContinuationInterceptor] as CoroutineDispatcher)
}

override fun finished(description: Description) {
super.finished(description)
Dispatchers.resetMain()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package ru.itis.karakurik.androidLab2.utils

import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException

fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS,
afterObserve: () -> Unit = {}
): T {
var data: T? = null
val latch = CountDownLatch(1)
val observer = object : Observer<T> {
override fun onChanged(o: T?) {
data = o
latch.countDown()
this@getOrAwaitValue.removeObserver(this)
}
}
this.observeForever(observer)

afterObserve.invoke()

// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
this.removeObserver(observer)
throw TimeoutException("LiveData value was never set.")
}

@Suppress("UNCHECKED_CAST")
return data as T
}