While I'm turning off internet connection on the phone I have an exception
Cannot evaluate the expression: : Backend Internal error: Exception during code generation
Cause: Can not generate outer receiver value for class <closure-StartViewModel$fetchCurrentWeek$1>
When internet is on, everething works fine!
Exception appears in function fetchCurrentWeek, on line
val currentWeek = WeekSource(ApiFactory.rozkladKpiApi).getCurrentWeek()
StartViewModel.kt
package andy.schedulekpi.ui.fragments.start
import android.content.SharedPreferences
import androidx.lifecycle.MutableLiveData
import andy.schedulekpi.network.api.ApiFactory
import andy.schedulekpi.network.sources.GroupSource
import andy.schedulekpi.network.sources.SourcesFactory
import andy.schedulekpi.network.sources.WeekSource
import andy.schedulekpi.ui.fragments.base.BaseViewModel
import andy.schedulekpi.utils.SHARED_PREFERENCES_GROUP
import kotlinx.coroutines.*
class StartViewModel : BaseViewModel() {
// TODO: Implement the ViewModel
private val source = SourcesFactory.weekSource
val mCurrentWeek : MutableLiveData<Int> = MutableLiveData()
fun fetchCurrentWeek() {
scope.launch {
val currentWeek = WeekSource(ApiFactory.rozkladKpiApi).getCurrentWeek()
mCurrentWeek.postValue(currentWeek)
}
}
fun getGroupFromSharedPreferences(sharedPreferences: SharedPreferences) : String {
return sharedPreferences.getString(SHARED_PREFERENCES_GROUP, "null")!!
}
fun isGroupCachedInSharedPreferences(sharedPreferences: SharedPreferences) : Boolean {
return sharedPreferences.contains(SHARED_PREFERENCES_GROUP)
}
}
Image:
image of debugger: https://ibb.co/fNSLZZM
Related
I'm trying to follow MVVM pattern to fetch data from the given api but getting error while Initiating a connection. My application gets crashed showing the error in the logcat.
My ModalClass.kt
package com.example.retrofitdemo2.api
class ModelClass : ArrayList<ModelClassItem>()
ModelClassItem:
package com.example.retrofitdemo2.api
data class ModelClassItem(
val body: String,
val id: Int,
val title: String,
val userId: Int
)
RetrofitHelperClass.kt
package com.example.retrofitdemo2.api
import com.google.gson.GsonBuilder
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
class RetrofitHelperClass {
companion object {
private val BASE_URL = "https://jsonplaceholder.typicode.com/"
var interceptor = HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY
}
var client = OkHttpClient.Builder().apply {
this.addInterceptor(interceptor)
}.build()
fun getInstance(): Retrofit {
return Retrofit.Builder().baseUrl(BASE_URL).client(client)
.addConverterFactory(GsonConverterFactory.create(GsonBuilder().create())).build()
}
}
}
RetrofitService.kt
package com.example.retrofitdemo2.api
import androidx.lifecycle.LiveData
import retrofit2.Response
import retrofit2.http.GET
interface RetrofitService {
#GET("/albums")
fun get(): LiveData<Response<ModelClass>>
}
Repository.kt: Here i'm getting errors which is mentioned at the end.
Seems like repository is unable to create Adapter call.enter code here
package com.example.retrofitdemo2.repository
import com.example.retrofitdemo2.api.RetrofitService
class Repositroy(retrofitService: RetrofitService) {
val response = retrofitService.get()
}
viewmodel.kt:
package com.example.retrofitdemo2.viewmodel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.retrofitdemo2.api.ModelClass
import com.example.retrofitdemo2.api.ModelClassItem
import com.example.retrofitdemo2.repository.Repositroy
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class MainViewModel(repositroy: Repositroy) : ViewModel() {
var getdata = repositroy.response
}
MainViewModelFactory.kt:
package com.example.retrofitdemo2.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.example.retrofitdemo2.repository.Repositroy
class MainViewModelFactory(private val repositroy: Repositroy) :
ViewModelProvider.Factory
{
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MainViewModel::class.java)){
return MainViewModel(repositroy) as T
}
throw IllegalArgumentException("Problem in View Model Factory")
}
}
MainActivity.kt:
package com.example.retrofitdemo2
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.retrofitdemo2.api.RetrofitHelperClass
import com.example.retrofitdemo2.api.RetrofitService
import com.example.retrofitdemo2.databinding.ActivityMainBinding
import com.example.retrofitdemo2.repository.Repositroy
import com.example.retrofitdemo2.viewmodel.MainViewModel
import com.example.retrofitdemo2.viewmodel.MainViewModelFactory
import retrofit2.Retrofit
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewmodel : MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
var retorservice =
RetrofitHelperClass.getInstance().create(RetrofitService::class.java)
val repositroy = Repositroy(retorservice)
val factory = MainViewModelFactory(repositroy)
viewmodel = ViewModelProvider(this, factory).get(MainViewModel::class.java)
binding.viewmodel = viewmodel
binding.lifecycleOwner = this
viewmodel.getdata.observe(this, Observer {
Log.i("MainActivity", "${it}")
})
}
}
Error:
Unable to start activity
ComponentInfo{com.example.retrofitdemo2/com.example.retrofitdemo2.MainActivity}:
java.lang.IllegalArgumentException: Unable to create call adapter for
androidx.lifecycle.LiveData<retrofit2.Response<com.example.retrof
at com.example.retrofitdemo2.repository.Repositroy.<init>(Repositroy.kt:7)
at com.example.retrofitdemo2.MainActivity.onCreate(MainActivity.kt:28)
at android.app.Activity.performCreate(Activity.java:8109)
at android.app.Activity.performCreate(Activity.java:8083)
I am currently trying to write an integration test for my repository layer that tests if I call a method, getExercises(), then it returns List<Exercise>, provided that the data is loaded into the local Firestore emulator ahead of time.
So far I got the local Firestore emulator to switch on and off at the beginning/end of a test run, respectively. I am able to populate my data into Firestore, and see the data in the local Firestore emulator via the web UI.
My problem is that my test assertion times out because the Task (an asynchronous construct the Firestore library uses), blocks the thread at the await() part in the repository method.
Test
package com.example.fitness.data
import androidx.test.ext.junit.runners.AndroidJUnit4
import app.cash.turbine.test
import com.example.fitness.Constants.EXERCISES_REF
import com.example.fitness.FirebaseEmulatorTest
import com.google.android.gms.tasks.Tasks
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.ExperimentalTime
#HiltAndroidTest
#RunWith(AndroidJUnit4::class)
class ExerciseRepositoryTest : FirebaseEmulatorTest() {
#get:Rule
var hiltRule = HiltAndroidRule(this)
#Inject
lateinit var subject: ExerciseRepository
#Before
fun setup() {
hiltRule.inject()
}
#ExperimentalTime
#Test
fun `#getExercises returns a flow of exercises`() = runBlocking {
val exercises = mutableListOf<Exercise>().apply {
add(Exercise("a", "pushups"))
add(Exercise("b", "pull-ups"))
add(Exercise("c", "sit-ups"))
}
runBlocking(Dispatchers.IO) {
val task1 = firestoreInstance.collection(EXERCISES_REF).add(exercises.first())
val task2 = firestoreInstance.collection(EXERCISES_REF).add(exercises[1])
val task3 = firestoreInstance.collection(EXERCISES_REF).add(exercises.last())
Tasks.await(task1)
Tasks.await(task2)
Tasks.await(task3)
println("Done with tasks: task1: ${task1.isComplete}. task2: ${task2.isComplete}. task3: ${task3.isComplete}.")
}
println("About to get exercises")
subject.getExercises().test(timeout = Duration.seconds(5)) {
println("test body")
assertThat(awaitItem().size, `is`(4)) // Just checking that it passes for the right reasons first. This number should be 3
}
}
}
Repository (System under test)
package com.example.fitness.data
import com.example.fitness.Constants.EXERCISES_REF
import com.google.firebase.firestore.CollectionReference
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Named
import javax.inject.Singleton
#Singleton
class ExerciseRepository #Inject constructor(
#Named(EXERCISES_REF) private val exerciseCollRef: CollectionReference
) {
fun getExercises() = flow<List<Exercise>> {
println("beginning of searchForExercise")
val exercises = exerciseCollRef.limit(5).get().await() // NEVER FINISHES!!
println("Exercise count: ${exercises.documents}")
emit(exercises.toObjects(Exercise::class.java))
}
}
The output of this results in:
Done with tasks: task1: true. task2: true. task3: true.
About to search for exercises
beginning of searchForExercise
test body
Timed out waiting for 5000 ms
kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 5000 ms
"Exercise count: 3" message never prints!
Note: I am using Robolectric 4.6.1, kotlinx-coroutines-playservices (1.5.0) to provide the await() extension function, and the Turbine testing library for flow assertions (0.6.1)
Perhaps of relevance is a superclass this test inherits that sets the main dispatcher to a test dispatcher.
package com.example.fitness
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.test.*
import org.junit.After
import org.junit.Before
import org.junit.Rule
abstract class CoroutineTest {
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
protected val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
private val testCoroutineScope = TestCoroutineScope(testDispatcher)
#Before
fun setupViewModelScope() {
Dispatchers.setMain(testDispatcher)
}
#After
fun cleanupViewModelScope() {
Dispatchers.resetMain()
}
#After
fun cleanupCoroutines() {
testDispatcher.cleanupTestCoroutines()
testDispatcher.resumeDispatcher()
}
fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineScope.runBlockingTest(block)
}
Any help here would be greatly appreciate.
Edit
I have opened an issue with the kotlin extensions team to get more visibility on how to go about testing this, including a repo demonstrating the problem.
This problem has been resolved in a new version of the kotlinx-coroutines package (1.6.0-RC). See my github compare across branches. Tests now pass as expected with this version.
I am trying to write a test for my View Model that verifies when I call setFirstTime, the state of the view model contains the updated value for firstTime set to false.
The UserPreferencesRepository provides a Flow of the preferences to the viewmodel, which exposes them as LiveData (using asLiveData extension).
Here is my test I am having trouble with:
MainViewModelTest.kt
package com.example.fitness.main
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.example.fitness.MainCoroutineRule
import com.example.fitness.data.UserPreferencesRepository
import com.example.fitness.getOrAwaitValue
import dagger.hilt.android.testing.HiltAndroidRule
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.hamcrest.CoreMatchers.`is`
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import javax.inject.Inject
#HiltAndroidTest
#RunWith(AndroidJUnit4::class)
class MainViewModelTest {
#get:Rule
var hiltRule = HiltAndroidRule(this)
#get:Rule
val instantExecutorRule = InstantTaskExecutorRule()
#get:Rule
#ExperimentalCoroutinesApi
var mainCoroutineRule = MainCoroutineRule()
private lateinit var mainViewModel: MainViewModel
#Inject
lateinit var userPreferencesRepository: UserPreferencesRepository
#Before
#ExperimentalCoroutinesApi
fun init() {
hiltRule.inject()
// Execute all pending coroutine actions in MainViewModel initialization
mainCoroutineRule.runBlockingTest {
mainViewModel = MainViewModel(userPreferencesRepository)
}
}
#ExperimentalCoroutinesApi
#Test
fun `#setFirstTime marks the user as have opened the app at least once`() {
assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(true))
mainCoroutineRule.runBlockingTest {
mainViewModel.setFirstTime()
}
# Failing assertion. Comes back as `true` when I expect it to be `false`
assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(false))
}
}
MainViewModel.kt
package com.example.fitness.main
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import com.example.fitness.data.UserPreferencesRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
#HiltViewModel
class MainViewModel #Inject constructor(
private val userPreferencesRepository: UserPreferencesRepository
) : ViewModel() {
val state = userPreferencesRepository.userPreferencesFlow.asLiveData()
/**
* Persists a value signifying that the user has started the app before.
*/
fun setFirstTime() {
viewModelScope.launch {
userPreferencesRepository.updateFirstTime(false)
}
}
}
UserPreferencesRepository
package com.example.fitness.data
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import javax.inject.Inject
data class UserPreferences(
val firstTime: Boolean
)
class UserPreferencesRepository #Inject constructor(private val dataStore: DataStore<Preferences>) {
private object PreferencesKeys {
val FIRST_TIME = booleanPreferencesKey("first_time")
}
val userPreferencesFlow: Flow<UserPreferences> = dataStore.data.map { preferences ->
val firstTime = preferences[PreferencesKeys.FIRST_TIME] ?: true
UserPreferences(firstTime)
}
suspend fun updateFirstTime(firstTime: Boolean) {
dataStore.edit { preferences ->
preferences[PreferencesKeys.FIRST_TIME] = firstTime
}
}
}
I verified via the debugger that the body of the dataStore.edit code is being run prior to the last assertion of the test. I also noticed that the body of dataStore.data.map is also being run after the update, with the correctly populated preferences set to false. It appears that running the test in debug mode and quickly stepping through my break points results in a passing test, but running the test normally produces a failure, which leads me to believe there is some race condition present.
I am basing my work off of a Google Codelab. Any help would be greatly appreciated.
I managed to determine what the issue was. When I am creating my DataStore in the app, I am using the default coroutine scope, which is Dispatchers.IO. In my tests, I was replacing the main coroutine with kotlinx.coroutines.test.TestCoroutineDispatcher, but I needed to somehow instantiate the DataStore with a TestCoroutineScope as well, so that those saving actions would run synchronously.
Taking a lot of liberties from this extremely helpful article, my final code looks like:
MainViewModelTest.kt
#RunWith(AndroidJUnit4::class)
class MainViewModelTest : DataStoreTest() {
private lateinit var mainViewModel: MainViewModel
#Before
fun init() = runBlockingTest {
val userPreferencesRepository = UserPreferencesRepository(dataStore)
mainViewModel = MainViewModel(userPreferencesRepository)
}
#Test
fun `#setFirstTime marks the user as having opened the app at least once`() = runBlockingTest {
assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(true))
mainViewModel.setFirstTime()
assertThat(mainViewModel.state.getOrAwaitValue().firstTime, `is`(false))
}
}
DataStoreTest.kt
abstract class DataStoreTest : CoroutineTest() {
private lateinit var preferencesScope: CoroutineScope
protected lateinit var dataStore: DataStore<Preferences>
#Before
fun createDatastore() {
preferencesScope = CoroutineScope(testDispatcher + Job())
dataStore = PreferenceDataStoreFactory.create(scope = preferencesScope) {
InstrumentationRegistry.getInstrumentation().targetContext.preferencesDataStoreFile(
"test-preferences-file"
)
}
}
#After
fun removeDatastore() {
File(
ApplicationProvider.getApplicationContext<Context>().filesDir,
"datastore"
).deleteRecursively()
preferencesScope.cancel()
}
}
CoroutineTest.kt
abstract class CoroutineTest {
#Rule
#JvmField
val rule = InstantTaskExecutorRule()
protected val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
private val testCoroutineScope = TestCoroutineScope(testDispatcher)
#Before
fun setupViewModelScope() {
Dispatchers.setMain(testDispatcher)
}
#After
fun cleanupViewModelScope() {
Dispatchers.resetMain()
}
#After
fun cleanupCoroutines() {
testDispatcher.cleanupTestCoroutines()
testDispatcher.resumeDispatcher()
}
fun runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) =
testCoroutineScope.runBlockingTest(block)
}
I'm fairly new to Android and Java / Kotlin so I've been struggling to implement cookies in the recommended architecture. I looked in many places, read the documentation and watched many videos and everyone had such different ways to implement things that I was still confused. How does it all fit together?
I would have thought this was such a common use case that I can't believe the answer isn't all over the net, but I've had to work hard to put all the pieces together. Below is what worked for me from the Repository down. I haven't included the database side of things since that is well documented in many places and I found it easy enough to follow (if anyone needs me to include that, let me know). I switched to Kotlin part way through because I could only find some parts of the answer in Java. My example is to log in a user and get basic profile details.
Repository sends login details to server and saves response in database then pulls that info to save as LiveData
package com.example.myapplication
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.example.myapplication.*
import com.example.myapplication.asDomainModel
import com.example.myapplication.asDBEntity
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import javax.inject.Inject
class LoginRepository #Inject constructor(
private val myDao: MyDao,
private val myNetwork: Network
) {
private val _profile: MutableLiveData<Profile> = MutableLiveData()
val profile: LiveData<Profile>
get() = _profile
suspend fun login(name: String, password: String) {
withContext(Dispatchers.IO) {
// log in to server and get profile data
val profileNWEntity = myNetwork.login("login", name, password)
// process response
when (profileNWEntity.status) {
"PROFLOGINOK" -> {
// save profile in database then retrieve
myDao.insertProfile(profileNWEntity.asDBEntity())
_profile.postValue(myDao.getProfile(profileNWEntity.user).asDomainModel())
}
else -> {
throw IOException (profileNWEntity.status)
}
}
}
}
}
Retrofit endpoint defines the login process
package com.example.myapplication
import com.example.myapplication.ProfileNWEntity
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.POST
interface Network {
#FormUrlEncoded
#POST("server_api")
suspend fun login(
#Field("action") action: String,
#Field("name") name: String,
#Field("pass") password: String
): ProfileNWEntity
}
Entity - used by Gson to parse the network response and by the repository to adapt for the database
package com.example.myapplication
import com.example.myapplication.AccountDBEntity
import com.example.myapplication.ProfileDBEntity
/**
* Base profile response from network query
*/
data class ProfileNWEntity(
val user: Int,
val name: String,
val status: String
)
// map the profile from network to database format
fun ProfileNWEntity.asDBEntity(): ProfileDBEntity {
return ProfileDBEntity(
id = user,
name = name
)
}
Retrofit class to enable inclusion of cookies (together with the interceptors included below, this comes from the work of tsuharesu and Nikhil Jha found at https://gist.github.com/nikhiljha/52d45ca69a8415c6990d2a63f61184ff)
package com.example.myapplication
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Inject
class RetrofitWithCookie #Inject constructor(
context: Context, // uses Hilt to inject the context to be passed to the interceptors
gson: Gson
) {
private val mContext = context
private val gson = gson
fun createRetrofit(): Retrofit {
val client: OkHttpClient
val builder = OkHttpClient.Builder()
builder.addInterceptor(AddCookiesInterceptor(mContext)) // VERY VERY IMPORTANT
builder.addInterceptor(ReceivedCookiesInterceptor(mContext)) // VERY VERY IMPORTANT
client = builder.build()
return Retrofit.Builder()
.baseUrl("myServer URL") // REQUIRED
.client(client) // VERY VERY IMPORTANT
.addConverterFactory(GsonConverterFactory.create(gson))
.build() // REQUIRED
}
}
Receiving Interceptor catches the inbound cookies and saves them in sharedpreferences
package com.example.myapplication
import android.content.Context
import androidx.preference.PreferenceManager
import okhttp3.Interceptor
import okhttp3.Response
import java.io.IOException
import java.util.*
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
class ReceivedCookiesInterceptor(context: Context?) : Interceptor {
private val context: Context?
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val originalResponse = chain.proceed(chain.request())
if (!originalResponse.headers("Set-Cookie").isEmpty()) {
val cookies = PreferenceManager.getDefaultSharedPreferences(context)
.getStringSet("PREF_COOKIES", HashSet()) as HashSet<String>?
for (header in originalResponse.headers("Set-Cookie")) {
cookies!!.add(header)
}
val memes = PreferenceManager.getDefaultSharedPreferences(context).edit()
memes.putStringSet("PREF_COOKIES", cookies).apply()
memes.commit()
}
return originalResponse
}
init {
this.context = context
} // AddCookiesInterceptor()
}
AddCookies interceptor adds the cookie back into future requests
package com.example.myapplication
import android.content.Context
import androidx.preference.PreferenceManager
import dagger.hilt.android.qualifiers.ActivityContext
import okhttp3.Interceptor
import okhttp3.Response
import timber.log.Timber
import java.io.IOException
import java.util.*
// Original written by tsuharesu
// Adapted to create a "drop it in and watch it work" approach by Nikhil Jha.
// Just add your package statement and drop it in the folder with all your other classes.
/**
* This interceptor put all the Cookies in Preferences in the Request.
* Your implementation on how to get the Preferences may ary, but this will work 99% of the time.
*/
class AddCookiesInterceptor(#ActivityContext context: Context?) : Interceptor {
// We're storing our stuff in a database made just for cookies called PREF_COOKIES.
// I reccomend you do this, and don't change this default value.
private val context: Context?
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val builder = chain.request().newBuilder()
val preferences = PreferenceManager.getDefaultSharedPreferences(context).getStringSet(
PREF_COOKIES, HashSet()
) as HashSet<String>?
// Use the following if you need everything in one line.
// Some APIs die if you do it differently.
/*String cookiestring = "";
for (String cookie : preferences) {
String[] parser = cookie.split(";");
cookiestring = cookiestring + parser[0] + "; ";
}
builder.addHeader("Cookie", cookiestring);
*/for (cookie in preferences!!) {
builder.addHeader("Cookie", cookie)
Timber.d("adding cookie %s", cookie)
}
return chain.proceed(builder.build())
}
companion object {
const val PREF_COOKIES = "PREF_COOKIES"
}
init {
this.context = context
}
}
Hilt Module to tie it all together
package com.example.myapplication
import android.content.Context
import com.example.myapplication.Network
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import javax.inject.Singleton
#InstallIn(SingletonComponent::class)
#Module
class NetworkModule {
#Singleton
#Provides
fun provideNetwork(retrofit: Retrofit)
: Network = retrofit.create(Network::class.java)
#Singleton
#Provides
fun provideRetrofitWithCookie(
#ApplicationContext context: Context,
gson: Gson
): Retrofit = RetrofitWithCookie(context, gson).createRetrofit()
#Singleton
#Provides
fun provideGson(): Gson = GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") // used for parsing other responses
.create()
}
I am developing an android with Junit5 and Mockito.
Some tests are ParameterizedTest and others are just Test.
Here is my sample code.
When I run this test, only "ParameterizedTests" run.
"JustTests" is not shown on the JUnit test console list.
How can I run "JustTests" too?
import org.junit.Test
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Nested
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import org.mockito.InOrder
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
internal class MyPresenterTest {
#Mock
private lateinit var view: MyContract.View
private lateinit var presenter: MyContract.Presenter
#BeforeEach
fun setup() {
MockitoAnnotations.openMocks(this)
presenter = MyPresenter(view)
}
#Nested
#DisplayName("Just Test")
inner class JustTests {
#DisplayName("test 1")
#Test
fun greetingTest1() {
...
}
}
#Nested
#DisplayName("Parameterized test")
inner class ParameterizedTests {
#ParameterizedTest(name = "{0}")
#ValueSource(strings = ["Hello", "Hi])
#Test
fun greetingTest2(greeting: String) {
...
}
}
}