I'm running an androidTest instrumentation test and I have a method that returns LiveData from a DAO object using Room.
I'm calling the method like so:
val animal = roomDatabase.animalsDao().getAnimal(1)
animal.observeForever(mMockObserver)
assertNotNull(animal.value)
I used Mockito to mock the observer:
#Mock
private lateinit var mMockObserver = Observer<Animal>
This should return an instance of LiveData containing the Animal at id 1, but it's null. It's my understanding that in order for LiveData to return anything, there must be an observer. Did I set this up incorrectly?
Note: If I change the signature of getAnimal() in the DAO to return an Animal directly, rather than a LiveData, then it works so I know it's something with LiveData.
After a little more digging I've found a utility method Google provided through their Architecture Components examples on GitHub.
LiveDataTestUtil
public class LiveDataTestUtil {
/**
* Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
* Once we got a notification via onChanged, we stop observing.
*/
public static <T> T getValue(final LiveData<T> liveData) throws InterruptedException {
final Object[] data = new Object[1];
final CountDownLatch latch = new CountDownLatch(1);
Observer<T> observer = new Observer<T>() {
#Override
public void onChanged(#Nullable T o) {
data[0] = o;
latch.countDown();
liveData.removeObserver(this);
}
};
liveData.observeForever(observer);
latch.await(2, TimeUnit.SECONDS);
//noinspection unchecked
return (T) data[0];
}
}
This allows you to pass the LiveData instance and get back the value it holds.
Update (JUnit 4):
You can also use the InstantTaskExecutorRule combined with observeForever to test your LiveData. In Kotlin you set #get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() at the top of your test class to ensure LiveData is handled synchronously, then inside your test cases myLiveData.observeForever { /* Do something when event emitted */ } to get the LiveData value.
Update (JUnit 5)
If you're using JUnit5, then you can use this extension instead of the Rule explained in Update (JUnit4) above.
class InstantTaskExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) {
runnable.run()
}
override fun postToMainThread(runnable: Runnable) {
runnable.run()
}
override fun isMainThread(): Boolean {
return true
}
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
Use this extension by annotating your test class like so:
#ExtendWith(InstantTaskExecutorExtension::class)
class MyTestClass { ... }
If you're new to extensions (they replace JUnit 4 Rules), you can find additional documentation here: https://junit.org/junit5/docs/current/user-guide/#extensions
If you are doing Kotlin, rather than Java, then you can also use:
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
// Original Java: https://github.com/googlesamples/android-architecture-components/blob/master/BasicSample/app/src/androidTest/java/com/example/android/persistence/LiveDataTestUtil.java
object LiveDataTestUtil {
/**
* Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
* Once we got a notification via onChanged, we stop observing.
*/
#Throws(InterruptedException::class)
fun <T> getValue(liveData: LiveData<T>): T? {
val data = arrayOfNulls<Any>(1)
val latch = CountDownLatch(1)
val observer: Observer<T?> = object : Observer<T?> {
override fun onChanged(o: T?) {
data[0] = o
latch.countDown()
liveData.removeObserver(this)
}
}
liveData.observeForever(observer)
latch.await(2, TimeUnit.SECONDS)
#Suppress("UNCHECKED_CAST")
return data[0] as T?
}
}
(At the moment the feature of A/S for automigration of Java to Kotlin doesn't quite work correctly for the Google class)
An example for part "Update (JUnit 4)" in Programmer001's answer. (Tuned with official doc on purpose)
Database item data class and table definition:
#Entity(tableName = "item")
data class Item (
var name: String,
#PrimaryKey(autoGenerate = true) var id: Int = 0
)
Test class:
package ...
import ...
#RunWith(AndroidJUnit4::class)
class DBInstrumentedTest1 {
#get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()
private lateinit var db: DB
#Before
private fun createDb(): DB {
db = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
DB::class.java
).build()
}
#After
#Throws(IOException::class)
fun closeDb() {
db.close()
}
#Test
#Throws(Exception::class)
fun coroutine_livedata_db_tests_work() {
val itemDao = db.getItemDao()
val item = Item(name = "First", id = 1)
runBlocking(Dispatchers.Default) { itemDao.insert(Item(item.name)) }
itemDao.getItemByName(item.name).asLiveData().observeForever {
assertThat(it, equalTo(item))
}
}
}
Related
I have ViewModel which exposes flow to fragment. I am calling API from ViewModel's init which emits different states. I am not able to write unit test to check all the emitted states.
My ViewModel
class FooViewModel constructor( fooProvider : FooProvider){
private val _uiState = MutableSharedFlow<UiState>(replay = 1)
// Used in fragment to collect ui states.
val uiState = _uiState.asSharedFlow()
init{
_uiState.emit(FetchingFoo)
viewModelScope.runCatching {
// Fetch shareable link from server [users.sharedInvites.list].
fooProvider.fetchFoo().await()
}.fold(
onSuccess = {
_uiState.emit(FoundFoo)
},
onFailure = {
_uiState.emit(EmptyFoo)
}
)
}
sealed class UiState {
object FetchingFoo : UiState()
object FoundFoo : UiState()
object EmptyFoo : UiState()
}
}
Now I want to test this ViewModel to check if all the states are being emitted.
My Test: Note I am using turbine library.
class FooViewModelTest{
#Mock
private lateinit var fooProvider : FooProvider
#Test
fun testFooFetch() = runTest {
whenever(fooProvider.fetchFoo()).thenReturn(// Expected API response)
val fooViewModel = FooViewModel(fooProvider)
// Here lies the problem. as we create fooViewModel object API is called.
// before reaching test block.
fooViewModel.uiState.test{
// This condition fails as fooViewModel.uiState is now at FoundFoo.
assertEquals(FetchingFoo, awaitItem())
assertEquals(FoundFoo, awaitItem())
}
}
}
How to delay init till inside on .test{} block.
Tried creating ViewModel object by Lazy{} but not working.
It is not very pragmatic to "delay" emissions for sake of testing, this may produce flakey tests.
This is more of a coding issue - the right question should be "Does this logic belong in the class initialisation. The fact that it is more difficult to test should give you hints that it is less than ideal.
A better solution would be to use a StateFlow which is lazily initialised something like (some code assumed for sake of testing) :
class FooViewModel constructor(private val fooProvider: FooProvider) : ViewModel() {
val uiState: StateFlow<UiState> by lazy {
flow<UiState> {
emit(FoundFoo(fooProvider.fetchFoo()))
}.catch { emit(EmptyFoo) }
.flowOn(Dispatchers.IO)
.stateIn(
scope = viewModelScope,
started = WhileSubscribed(5_000),
initialValue = FetchingFoo)
}
sealed class UiState {
object FetchingFoo : UiState()
data class FoundFoo(val list: List<Any>) : UiState()
object EmptyFoo : UiState()
}
}
fun interface FooProvider {
suspend fun fetchFoo(): List<Any>
}
Then testing could be something like :
class FooViewModelTest {
#ExperimentalCoroutinesApi
#Test fun `whenObservingUiState thenCorrectStatesObserved`() = runTest {
val states = mutableListOf<UiState>()
FooViewModel { emptyList() }
.uiState
.take(2)
.toList(states)
assertEquals(2, states.size)
assertEquals(listOf(FetchingFoo, FoundFoo(emptyList()), states)
}
#ExperimentalCoroutinesApi
#Test fun `whenObservingUiStateAndErrorOccurs thenCorrectStatesObserved`() = runTest {
val states = mutableListOf<UiState>()
FooViewModel { throw IllegalStateException() }
.uiState
.take(2)
.toList(states)
assertEquals(2, states.size)
assertEquals(listOf(FetchingFoo, EmptFoo), states)
}
}
addotional test dependencies :
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.1'
testImplementation "android.arch.core:core-testing:1.1.1"
I have an issue in unit test for a function used Coroutine to call API with networkBoundResource.
The issue is when run the test the API actually called, although it's supposed to return the expected response such as I determined in this line: whenever(mfSDKPaymentRepository.sendPayment(request)).thenReturn(expectedResponse)
This is the function want to test:
fun callSendPayment(
coroutineScope: CoroutineScope? = GlobalScope,
request: MFSendPaymentRequest,
apiLang: String,
listener: (MFResult<MFSendPaymentResponse>) -> Unit
) {
Const.apiLang = apiLang
coroutineScope?.launch(Dispatchers.Main) {
val dataResource = networkBoundResource {
mInteractors.sendPayment(request)
}
when (dataResource) {
is MFResult.Success ->
listener.invoke(MFResult.Success(dataResource.value.response!!))
is MFResult.Fail ->
listener.invoke(MFResult.Fail(dataResource.error))
}
}
}
This is the test class:
class MFSDKMainTest {
private val mfSDKPaymentRepository = mock<MFSDKPaymentGateWay>()
private val testScope = TestCoroutineScope()
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
#Before
fun setup() {
Dispatchers.setMain(Dispatchers.Unconfined)
}
#After
fun tearDown() {
Dispatchers.resetMain()
testScope.cleanupTestCoroutines()
}
#Test
fun testCallSendPayment() = runBlockingTest {
val data = MFSendPaymentResponse(invoiceId = ID)
val expectedResponse = SDKSendPaymentResponse(data)
val request = MFSendPaymentRequest(
0.100,
"Customer name",
MFNotificationOption.LINK
)
val lang = MFAPILanguage.EN
whenever(mfSDKPaymentRepository.sendPayment(request))
.thenReturn(expectedResponse)
MFSDKMain.callSendPayment(testScope, request, lang) {
assert(it is MFResult.Success)
}
}
}
In your function when you call
mInteractors.sendPayment(request)
how do you get a refrence to MFSDKPaymentGateWay? In your test method you are not setting the mocked object in MFSDKMain class.
If you are creating it inside the same class (which i assume is an Object class) you may need to find a way to mock object class. Probably you need to use mockk instead of mockito
If you have setter method for MFSDKPaymentGateWay you should call it in your test method.
Disclaimer: This is not actual code from any app, but an example of the flow and my current understanding on how best to do this. I am looking for help improving upon or what I am doing wrong.
I am trying to figure out the best way to structure an android application using the new jetpack viewModels, realm, and coroutines. I put together a gist of the flow that I have so far, and would love some feedback on how I can improve, what I could change, or what I am doing wrong. Ideally with examples or direct changes to my code.
It works as is, I am just not sure if I am using coroutines correctly or efficiently, and if there is a better way to structure the DAO's so that Realm can be injected for better testability. Someone has already mentioned changing the DAO to extend the LiveData<>, and using onActive() and onInactive() for posting the object. Is that a good idea?
// About Model is the model used by Realm. These models contains realm specific types, like RealmList
open class AboutModel(
var name: String = "",
#PrimaryKey
var version: String = ""
): RealmObject() {
/**
* Conversion function, to convert the view model layer object to the data layer object
*/
companion object {
fun from(about: About): AboutModel = AboutModel(about.name, about.version)
}
fun toObject(): About =
About(
this.name,
this.version
)
}
// About class used everywhere outside of the data/realm layer.
// Lines up with the AboutModel class, but free of realm or any other database specific types.
// This way, realm objects are not being referenced anywhere else. In case I ever need to
// replace realm for something else.
class About (val name: String = "Test", val version: String = "1.0.0") {
override fun toString(): String {
return "author is : $name, version is: $version"
}
}
// Couldn't inject the realm instance because its thread would not match with a suspend function.
// Even if both where background threads. Would be better if I could inject it, but couldn't get
// that to work.
class AboutDao() {
private val _about = MutableLiveData<About>()
init {
val realm = Realm.getDefaultInstance()
val aboutModel = realm.where(AboutModel::class.java).findFirst()
_about.postValue(aboutModel?.toObject() ?: About())
realm.close()
}
suspend fun setAbout(about: About) = withContext(Dispatchers.IO) {
val realm: Realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.copyToRealmOrUpdate(AboutModel.from(about))
_about.postValue(about)
}
realm.close()
}
fun getAbout() = _about as LiveData<About>
}
// Database is a singleton instance, so there is only ever one instance of the DAO classes
class Database private constructor() {
var aboutDao = AboutDao()
private set
companion object {
// #Volatile - Writes to this property are immediately visible to other threads
#Volatile private var instance: Database? = null
suspend fun getInstance() = withContext(Dispatchers.IO) {
return#withContext instance ?: synchronized(this) {
instance ?: Database().also { instance = it }
}
}
}
}
// Repo maintains the dao access. Is also setup to run as a singleton
class AboutRepo private constructor(private val aboutDao: AboutDao){
// This may seem redundant.
// Imagine a code which also updates and checks the backend.
suspend fun set(about: About) {
aboutDao.setAbout(about)
}
suspend fun getAbout() = aboutDao.getAbout()
companion object {
// Singleton instantiation you already know and love
#Volatile private var instance: AboutRepo? = null
fun getInstance(aboutDao: AboutDao) =
instance ?: synchronized(this) {
instance ?: AboutRepo(aboutDao).also { instance = it }
}
}
}
// Injector is used to help keep the injection in a single place for the fragments and activities.
object Injector {
// This will be called from About Fragment
suspend fun provideAboutViewModelFactory(): AboutViewModelFactory = withContext(Dispatchers.Default) {
AboutViewModelFactory(getAboutRepo())
}
private suspend fun getAboutRepo() = withContext(Dispatchers.IO) {
AboutRepo.getInstance(Database.getInstance().aboutDao)
}
}
// AboutViewModel's Factory. I found this code online, as a helper for injecting into the viewModel's factory.
class AboutViewModelFactory (private val aboutRepo: AboutRepo)
: ViewModelProvider.NewInstanceFactory() {
#Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return AboutViewModel(aboutRepo) as T
}
}
// About Fragments ViewModel
class AboutViewModel(private val aboutRepo: AboutRepo) : ViewModel() {
suspend fun getAbout() = aboutRepo.getAbout()
suspend fun setAbout(about: About) = aboutRepo.set(about)
}
// Fragment's onActivityCreated, I set the viewModel and observe the model from the view model for changes
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
lifecycleScope.launch {
viewModel = ViewModelProviders.of(
this#AboutFragment,
Injector.provideAboutViewModelFactory()
).get(AboutViewModel::class.java)
withContext(Dispatchers.Main) {
viewModel.getAbout().observe(viewLifecycleOwner, Observer { about ->
version_number.text = about?.version
})
}
}
}
I successfully did some tests of asynchronous function with only one callback interface as parameter with mockito-kotlin library but when I try to do a test of same function with another parameter like a String or Integer I receive error:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers!
2 matchers expected, 1 recorded:
-> at com.example.presentation.presenter.MyCollectionPresenterTest.getComicListByHeroOK(MyCollectionPresenterTest.kt:97)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
For more info see javadoc for Matchers class.
I´m sure I´m mocking properly callback interface with any() but I don´t know if I´m mocking integer parameter correctly. I tried with any(), anyInt(), eq(1) and any() as Int but always the same error.
Here is the class that I want to test:
#PerFragment
class MyCollectionPresenter #Inject constructor(private val useCase: GetComicListByHeroUseCase) {
#Inject
lateinit var view: MyCollectionView
lateinit var models: List<ComicModel>
fun getComicListByHero(heroId: Int) {
useCase.execute(heroId, object : HeroUseCase.GetComicListByHeroCallback {
override fun onComicListReceived(comicList: List<Comic>) {
models = ComicModelMapper.toModel(comicList)
view.setItems(models)
}
override fun onError() {
view.showMessage()
}
})
}
}
And this is the test class:
class MyCollectionPresenterTest : UnitTest() {
private lateinit var presenter: MyCollectionPresenter
#Mock
private lateinit var useCase: GetComicListByHeroUseCase
#Mock
private lateinit var view: MyCollectionView
#Before
fun setUp() {
presenter = MyCollectionPresenter(useCase)
initializeView()
}
#Test
fun getComicListByHeroOK() {
setupGetComicsCallbackOK()
presenter.getComicListByHero(any())
verify(presenter.view).setItems(emptyList())
}
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(any())
verify(presenter.view).showMessage()
}
private fun initializeView() {
presenter.view = view
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(any(), any())
}
private fun setupGetComicsCallbackOK() {
doAnswer {
val callback = it.arguments[0] as HeroUseCase.GetComicListByHeroCallback
callback.onComicListReceived(emptyList())
null
}.`when`(useCase).execute(any(), any())
}
}
This is base unit test class:
#RunWith(MockitoJUnitRunner::class)
abstract class UnitTest {
#Suppress("LeakingThis")
#Rule
#JvmField
val injectMocks = InjectMocksRule.create(this#UnitTest)
}
And this is a class for inject mocks rule:
class InjectMocksRule {
companion object {
fun create(testClass: Any) = TestRule { statement, _ ->
MockitoAnnotations.initMocks(testClass)
statement
}
}
}
Thank you very much for your help and excuse my english.
Regards!
UPDATE: I found the solution and posted as answer.
Finally, I know what I was doing wrong. First at all, it.argument[1] because callback is the second parameter of the function that I want to mock the answer.
And the other thing is that I was mocking the parameter of the function that I want to test presenter.getComicListByHero(1).
Here is the revised code:
#Test
fun getComicListByHeroError() {
setupGetComicsCallbackError()
presenter.getComicListByHero(1)
verify(presenter.view).showMessage()
}
private fun setupGetComicsCallbackError() {
doAnswer {
val callback = it.arguments[1] as HeroUseCase.GetComicListByHeroCallback
callback.onError()
null
}.`when`(useCase).execute(ArgumentMatchers.anyInt(), any())
}
Thank you very much to #voghDev for his help
I'm currently developing an app using the newly Android Architecture Components. Specifically, I'm implementing a Room Database that returns a LiveData object on one of its queries. Insertion and querying work as expected, however I have an issue testing the query method using a unit test.
Here is the DAO I'm trying to test:
NotificationDao.kt
#Dao
interface NotificationDao {
#Insert
fun insertNotifications(vararg notifications: Notification): List<Long>
#Query("SELECT * FROM notifications")
fun getNotifications(): LiveData<List<Notification>>
}
As you can tell, the query function returns a LiveData object, if I change this to be just a List, Cursor, or basically whatever then I get the expected result, which is the data inserted in the Database.
The issue is that the following test will always fail because the value of the LiveData object is always null:
NotificationDaoTest.kt
lateinit var db: SosafeDatabase
lateinit var notificationDao: NotificationDao
#Before
fun setUp() {
val context = InstrumentationRegistry.getTargetContext()
db = Room.inMemoryDatabaseBuilder(context, SosafeDatabase::class.java).build()
notificationDao = db.notificationDao()
}
#After
#Throws(IOException::class)
fun tearDown() {
db.close()
}
#Test
fun getNotifications_IfNotificationsInserted_ReturnsAListOfNotifications() {
val NUMBER_OF_NOTIFICATIONS = 5
val notifications = Array(NUMBER_OF_NOTIFICATIONS, { i -> createTestNotification(i) })
notificationDao.insertNotifications(*notifications)
val liveData = notificationDao.getNotifications()
val queriedNotifications = liveData.value
if (queriedNotifications != null) {
assertEquals(queriedNotifications.size, NUMBER_OF_NOTIFICATIONS)
} else {
fail()
}
}
private fun createTestNotification(id: Int): Notification {
//method omitted for brevity
}
So the question is: Does anyone knows of a better way to perform unit tests that involve LiveData objects?
Room calculates the LiveData's value lazily when there is an observer.
You can check the sample app.
It uses a getValue utility method which adds an observer to get the value:
public static <T> T getOrAwaitValue(final LiveData<T> liveData) throws InterruptedException {
final Object[] data = new Object[1];
final CountDownLatch latch = new CountDownLatch(1);
Observer<T> observer = new Observer<T>() {
#Override
public void onChanged(#Nullable T o) {
data[0] = o;
latch.countDown();
liveData.removeObserver(this);
}
};
liveData.observeForever(observer);
latch.await(2, TimeUnit.SECONDS);
//noinspection unchecked
return (T) data[0];
}
Better w/ kotlin, you can make it an extensions function :).
When you return a LiveData from a Dao in Room it makes the query asynchronously, and as #yigit said Room sets the LiveData#value lazily after you kick off the query by observing the LiveData. This pattern is reactive.
For unit tests you want the behavior to be synchronous, so you must block the test thread and wait for the value to be passed to the observer, then grab it from there and then you can assert on it.
Here's a Kotlin extension function for doing this:
private fun <T> LiveData<T>.blockingObserve(): T? {
var value: T? = null
val latch = CountDownLatch(1)
val observer = Observer<T> { t ->
value = t
latch.countDown()
}
observeForever(observer)
latch.await(2, TimeUnit.SECONDS)
return value
}
You can use it like this:
val someValue = someDao.getSomeLiveData().blockingObserve()
I found Mockito is very helpful in such case. Here is an example:
1.Dependencies
testImplementation "org.mockito:mockito-core:2.11.0"
androidTestImplementation "org.mockito:mockito-android:2.11.0"
2.Database
#Database(
version = 1,
exportSchema = false,
entities = {Todo.class}
)
public abstract class AppDatabase extends RoomDatabase {
public abstract TodoDao todoDao();
}
3.Dao
#Dao
public interface TodoDao {
#Insert(onConflict = REPLACE)
void insert(Todo todo);
#Query("SELECT * FROM todo")
LiveData<List<Todo>> selectAll();
}
4.Test
#RunWith(AndroidJUnit4.class)
public class TodoDaoTest {
#Rule
public TestRule rule = new InstantTaskExecutorRule();
private AppDatabase database;
private TodoDao dao;
#Mock
private Observer<List<Todo>> observer;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getTargetContext();
database = Room.inMemoryDatabaseBuilder(context, AppDatabase.class)
.allowMainThreadQueries().build();
dao = database.todoDao();
}
#After
public void tearDown() throws Exception {
database.close();
}
#Test
public void insert() throws Exception {
// given
Todo todo = new Todo("12345", "Mockito", "Time to learn something new");
dao.selectAll().observeForever(observer);
// when
dao.insert(todo);
// then
verify(observer).onChanged(Collections.singletonList(todo));
}
}
Hope this help!
As #Hemant Kaushik said, in this case you SHOULD use InstantTaskExecutorRule.
From developer.android.com:
A JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously.
It really works!
Slightly different approach than other answers might be to use https://github.com/jraska/livedata-testing.
You avoid mocking and the test can use API similar to RxJava testing and also you can get advantage from Kotlin extension functions.
NotificationDaoTest.kt
val liveData = notificationDao.getNotifications()
liveData.test()
.awaitValue() // for the case where we need to wait for async data
.assertValue { it.size == NUMBER_OF_NOTIFICATIONS }
If you are using JUnit 5, since rules are not applicable to it, thanks to this article you can manually create the extension:
class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
override fun beforeEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
override fun executeOnDiskIO(runnable: Runnable) = runnable.run()
override fun postToMainThread(runnable: Runnable) = runnable.run()
override fun isMainThread(): Boolean = true
})
}
override fun afterEach(context: ExtensionContext?) {
ArchTaskExecutor.getInstance().setDelegate(null)
}
}
and then in your test class use it like this:
#ExtendWith(InstantExecutorExtension::class /* , Other extensions */)
class ItemDaoTests {
...
}