Skip to content

Commit

Permalink
Merge branch 'master' into feature/routing
Browse files Browse the repository at this point in the history
# Conflicts:
#	app/build.gradle
#	app/src/main/java/com/cobeisfresh/template/di/PresentationModule.kt
#	app/src/main/java/com/cobeisfresh/template/ui/weather/presentation/WeatherViewModel.kt
#	data/build.gradle
#	domain/build.gradle
  • Loading branch information
LukaKordic committed Jul 29, 2019
2 parents 4970195 + c409654 commit cb6bd28
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 18 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,13 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:$mockitoVersion"
testImplementation "android.arch.core:core-testing:$architectureComponents"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion"

//di
implementation "org.koin:koin-android:$koinVersion"
implementation "org.koin:koin-androidx-viewmodel:$koinVersion"
implementation "org.koin:koin-androidx-scope:$koinVersion"

//arhitecture components
implementation "android.arch.lifecycle:viewmodel:$architectureComponents"
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/com/cobeisfresh/template/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import com.example.data.di.networkingModule
import com.example.data.di.repositoryModule
import com.example.domain.di.interactionModule
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.logger.Level

class App : Application() {

Expand All @@ -21,6 +23,7 @@ class App : Application() {

startKoin {
androidContext(this@App)
if (BuildConfig.DEBUG) androidLogger(Level.DEBUG)
modules(appModules + domainModules + dataModules)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.cobeisfresh.template.common.coroutine

import kotlinx.coroutines.Dispatchers
import kotlin.coroutines.CoroutineContext

open class CoroutineContextProvider {
open val main: CoroutineContext by lazy { Dispatchers.Main }
open val io: CoroutineContext by lazy { Dispatchers.IO }
open val default: CoroutineContext by lazy { Dispatchers.Default }
}

class TestCoroutineContextProvider : CoroutineContextProvider() {
override val main: CoroutineContext = Dispatchers.Unconfined
override val io: CoroutineContext = Dispatchers.Unconfined
override val default: CoroutineContext = Dispatchers.Unconfined
}
2 changes: 2 additions & 0 deletions app/src/main/java/com/cobeisfresh/template/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package com.cobeisfresh.template.di

import androidx.appcompat.app.AppCompatActivity
import com.cobeisfresh.template.routing.AppFragmentNavigator
import com.cobeisfresh.template.common.coroutine.CoroutineContextProvider
import com.cobeisfresh.template.routing.AppNavigator
import org.koin.dsl.module

val appModule = module {
single { CoroutineContextProvider() }
single { (activity: AppCompatActivity) -> AppNavigator(activity) }
single { AppFragmentNavigator() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val presentationModule = module {
viewModel { WeatherViewModel(get()) }
viewModel { WeatherViewModel(get(), get()) }
viewModel { WelcomeViewModel() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.cobeisfresh.template.common.DEFAULT_CITY_NAME
import com.cobeisfresh.template.ui.base.ViewState
import com.cobeisfresh.template.common.coroutine.CoroutineContextProvider
import com.cobeisfresh.template.ui.weather.base.ViewState
import com.example.domain.interaction.weather.GetWeatherUseCase
import com.example.domain.model.WeatherInfo
import com.example.domain.model.onFailure
import com.example.domain.model.onSuccess
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

class WeatherViewModel(private val getWeather: GetWeatherUseCase) : ViewModel() {
class WeatherViewModel(private val getWeather: GetWeatherUseCase,
private val coroutineContextProvider: CoroutineContextProvider) : ViewModel(), KoinComponent {

// we make this private and provide only immutable live data to observers so they can't change anything
private val _weatherLiveData = MutableLiveData<ViewState<WeatherInfo>>()
val weatherLiveData: LiveData<ViewState<WeatherInfo>>
get() = _weatherLiveData

fun getWeatherForLocation(location: String = DEFAULT_CITY_NAME) = viewModelScope.launch {
_weatherLiveData.value = ViewState.loading()
withContext(Dispatchers.IO) {
getWeather(location)
.onSuccess { _weatherLiveData.postValue(ViewState.success(it)) }
.onFailure { _weatherLiveData.postValue(ViewState.error(it.throwable)) }
}
}
fun getWeatherForLocation(location: String = DEFAULT_CITY_NAME) =
viewModelScope.launch(coroutineContextProvider.main) {
_weatherLiveData.value = ViewState.loading()
getWeather(location)
.onSuccess { _weatherLiveData.value = ViewState.success(it) }
.onFailure { _weatherLiveData.value = ViewState.error(it.throwable) }
}
}
6 changes: 6 additions & 0 deletions app/src/test/java/com/cobeisfresh/TestUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.cobeisfresh

const val TEMP = 20.0
const val HUMIDITY = 20
const val PRESSURE = 1013.30
const val OSIJEK_CITY_NAME = "Osijek"
36 changes: 32 additions & 4 deletions app/src/test/java/com/cobeisfresh/WeatherViewModelTest.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
package com.cobeisfresh

import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.cobeisfresh.template.common.coroutine.TestCoroutineContextProvider
import com.cobeisfresh.template.ui.weather.base.ViewState
import com.cobeisfresh.template.ui.weather.presentation.WeatherViewModel
import com.example.domain.interaction.weather.GetWeatherUseCase
import com.example.domain.model.Success
import com.example.domain.model.WeatherInfo
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import org.mockito.junit.MockitoJUnit
import org.mockito.junit.MockitoRule

@RunWith(MockitoJUnitRunner::class)
class WeatherViewModelTest {


private val getWeather: GetWeatherUseCase = mock()
private val coroutineContext = TestCoroutineContextProvider()
private val weatherViewModel by lazy { WeatherViewModel(getWeather, coroutineContext) }

@get:Rule
val rule: TestRule = InstantTaskExecutorRule()
@get:Rule
val mockitoRule: MockitoRule = MockitoJUnit.rule()

@Test
fun `test getWeather sets liveData value when success`() = runBlocking {
whenever(getWeather(OSIJEK_CITY_NAME)).thenReturn(Success(WeatherInfo(TEMP, HUMIDITY, PRESSURE)))
weatherViewModel.getWeatherForLocation(OSIJEK_CITY_NAME)
assertEquals(ViewState.Status.SUCCESS, weatherViewModel.weatherLiveData.value?.status)
}
}
1 change: 1 addition & 0 deletions data/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,5 @@ dependencies {
//di
implementation "org.koin:koin-android:$koinVersion"
implementation "org.koin:koin-androidx-viewmodel:$koinVersion"
implementation "org.koin:koin-androidx-scope:$koinVersion"
}
2 changes: 1 addition & 1 deletion data/src/main/java/com/example/data/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ import com.example.domain.repository.WeatherRepository
import org.koin.dsl.module

val repositoryModule = module {
single<WeatherRepository> { WeatherRepositoryImpl(get(), get()) }
factory<WeatherRepository> { WeatherRepositoryImpl(get(), get()) }
}
1 change: 1 addition & 0 deletions domain/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,5 @@ dependencies {
//di
implementation "org.koin:koin-android:$koinVersion"
implementation "org.koin:koin-androidx-viewmodel:$koinVersion"
implementation "org.koin:koin-androidx-scope:$koinVersion"
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ import org.koin.dsl.module

val interactionModule = module {
factory<GetWeatherUseCase> { GetWeatherUseCaseImpl(get()) }
}
}

0 comments on commit cb6bd28

Please sign in to comment.