Cannot invoke observeForever on a background thread - android

I've been using an observeForever() method as described here to test Room and LiveData for a while, and it has worked flawlessly. But when I changed to Android Studio 3.2 (or if it was the androidx refactoring, not sure), that method suddenly stopped working, throwing a
java.lang.IllegalStateException: Cannot invoke observeForever on a background thread
How can we fix this?

I solved it by adding the rule InstantTaskExecutorRule. According to the docs it will
A JUnit Test Rule that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously.
So one needs to add
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
to the test class for it to work. The Java equivalent would be
#Rule
public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule();
You will also need to add
androidTestImplementation "androidx.arch.core:core-testing:2.0.0"
to your models build.gradle dependencies.

As a beginner to this approach, accepted answer was a little bit vague for me. So just trying to explain it
add this in your build.gradle
androidTestImplementation "androidx.arch.core:core-testing:2.0.0
Now we need to add rule on test function. Lets say that I have a test function writeAndReadCategory then it will look like this in kotlin
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
#Test
fun writeAndReadCategory() {
....
}

Related

How to set an order in instrumented test over the Test Class?

I'd like to set an order over the Test Class.
#RunWith(AndroidJUnit4::class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
class ATest {
#Test
fun test0000()
#Test
fun test0001()
}
#RunWith(AndroidJUnit4::class)
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
class BTest {
#Test
fun test0002()
#Test
fun test0003()
}
I'd like to test ATest.test0000 -> ATest.test0001 -> BTest.test0002 -> BTest.test0003
Because ATest class must be tested before B Test.
How can I do that? Is it possible?
Firstly, I would recommend you to not have any dependencies in Tests.
i.e. Test A class and Test B class should run independently from each other.
This really helps when your application grows.
There should not be a condition that one test should run before another.
Only in a rare / genuine scenario we should have such dependency on sequence.
Because if you design your test with sequence related dependency then it will be difficult for you to maintain your test cases and it will get difficult when you follow Test Driven Development(TDD).
For above case, please try using SuiteClasses.
The SuiteClasses annotation specifies the Suite runner which test classes to include in this suite and in which order.
Please refer to the sample provided by Junit Team HERE

Using InstantTaskExecutorRule causes java.lang.RuntimeException in junit test

I am unit-testing a library that uses a RoomDatabase. Unit-testing the RoomDatabase itself was done using InstantTaskExecutorRule so that LiveData updates can occur instantaneously:
#Rule public TestRule rule = new InstantTaskExecutorRule();
And that works fine. However, when unit-testing the library which calls the database, using the same rule causes the test to throw the exception:
java.lang.RuntimeException: Exception while computing database live data.
...
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Is there any reason why InstantTaskExecutorRule works on the underlying database DAOs but not a library calling them?
I am also unit testing a RoomDatabase with Robolectric and the same error occurs while using
#get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
I simply removed it and I'm using runBlocking{ ... } in my tests like this example :
#Test
#Throws(Exception::class)
fun `test insert and retrieve text`() {
runBlocking {
dao.insertText("some text")
assertEquals("some text",dao.getText())
}
}
The only reason I can think of causing this error is a missing ".allowMainThreadQueries()" when building your database:
database = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
myDatabase::class.java
)
.allowMainThreadQueries()
.build()
(Also, testing with databases is recommended to be done as instrumented tests as local tests will use a default SQLite version instead of the version your app is actually using.)

Koin tests - DependencyResolutionException

I have multiple test classes with multiple tests in each class.
In each class I want to make sure that I get fresh test dependencies for each test. So I prepare my tests like this:
#Before
fun initTest() {
loadKoinModules(listOf(module {
scope(TEST_SCOPE, override = true) { Dependency1() }
scope(TEST_SCOPE, override = true) { Dependency2() }
}))
getKoin().createScope(TEST_SCOPE)
}
#After
fun shutdown() {
getKoin().getScope(TEST_SCOPE).close()
}
And it works very well when I run only the tests in that particular test class.
But when I run all my tests in the same time, and if multiple test classes have the same dependencies in their modules, I get an Exception like this:
org.koin.error.DependencyResolutionException: Multiple definitions found - Koin can't choose between :
Scope [name='Dependency2',class='package.Dependency2']
Scope [name='Dependency2',class='package.Dependency2']
Check your modules definition, use inner modules visibility or definition names.
So I fixed this by simply calling stopKoin() at the end of my shutdown method.
And so far I haven't noticed that my tests run much slower. So basically my question is: is this the preferred way to use Koin in my tests? Am I missing something or not using Koin Properly ?
I realize that this is more a code review/advice question than a real problem but I think this might still be useful to others.
Thanks

Testing LiveData using PowerMockRunner

My local unit tests use LiveData all the time. Normally, when you try to set a value on MutableLiveData you get
java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked.
because local JVM has no access to Android framework. I fixed that using that:
#get:Rule
val rule = InstantTaskExecutorRule()
Everything was fine, until I had to use PowerMockito to mock a static method from google play library. Since I added
#RunWith(PowerMockRunner::class)
#PrepareForTest(Tasks::class)
above my test class declaration I started to get this Looper not mocked error again. I used this rule before with MockitoJUnitRunner and everything was fine.
A bit late for the answer, but just faced the same issue and solved it!
To use PowerMock and InstantTaskExecutorRule you need to add the following annotation:
#RunWith(PowerMockRunner::class)
#PowerMockRunnerDelegate(MockitoJUnitRunner::class) //this line allows you to use the powermock runner and mockito runner
#PrepareForTest(UnderTestClass::class)
class UnderTestClassTest {
#get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
No need to fret as it turns out you can still use this method to test your LiveData observers!
First, add this dependency in your module’s build.gradle file:
testImplementation 'android.arch.core:core-testing:1.0.0-alpha3'
Make sure you use the same version as the rest of your android.arch.* dependencies!
Then, in the test class where you need to call setValue() and assert, add this field:
#Rule
public TestRule rule = new InstantTaskExecutorRule();
For Kotlin
#get:Rule
var rule: TestRule = InstantTaskExecutorRule()
Behind the scenes, this bypasses the main thread check, and immediately runs any tasks on your test thread, allowing for immediate and predictable calls and therefore assertions.
Already have this answer here.

Android plain Junit with Dagger 2

I used to work in MVP and I usually test my presenters using a plain Junit (Not the Instrumentation !) , since Presenters only have the business logic and no references to Android internals whatsoever.
Now by switching to Dagger 2 , I understood that I have a problem setting up a "TestModule" for my app component.
Creating a component will not work from within a test class (probably because "apt" is not running there)
Didn't find any examples for using Dagger with a standard Junit testing. Every example I have found only relies on Instrumentation testing or Roboelectric (which basically mocks Activities and other Android related stuff) , but this is just a UI testing for me , and I don't need that.
Just to make things clear , I am talking about the tests that are located at app->src->test folder not the app->src->androidTest !
So do I do something wrong ? Or missing something ? Can anyone explain or give examples on how to use Dagger 2 in normal unit tests ?
I'm not sure if my solution will work for you but I see no reason it shouldn't.
First I created testInjectionComponent
#Singleton
#Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}
Then my Unit Tests I add injection in the before method. like so:
#Before
public void setUp() throws Exception {
MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
.builder()
.mockNetworkModule(new MockNetworkModule())
.build();
mockInjectionComponent.inject(this);
}
Then I just Annotate my Injected Object.
EDIT :
Do not forget to add testApt "com.google.dagger:dagger-compiler:$daggerVersion" at your app.gradle file .
As mentioned by the accepted answer. Do not forget to add :
For Java
Android Test
androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'
For Kotlin
Android Test
kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'
JUnit test
kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'
You don't need any dagger to test your presenter. Dagger's job is it to fullfill the dependencies of your classes (dependency injection).
For example you have this Presenter:
public class MyPresenter {
Database database;
ApiService apiService;
#Inject
public MyPresenter(final Database database, final ApiService apiService) {
this.database = database;
this.apiService = apiService;
}
}
Dagger will provide your Presenter with the database and apiService objects for your presenter to use them. When running the actual app (not a test) these will be real objects with real functionality.
When testing the presenter, you want to test only the presenter, everything else should be mocked.
So when you create the presenter in your PresenterTest, you create it with mocked versions of database and apiService.
You can then test how your presenter interacts with these object by
a. mocking the objects behaviour like
when(database.getSomething()).thenReturn(something)
b. verify your presenter does what you want it to do with these objects like
verify(database).saveSomething()
(pseudo code)
Standard way to mock would be Mockito.
You can swap out real modules with fake modules in two ways: do it at compile time using flavors as recommended by google architecture samples or at runtime by creating an abstract method which injects the real thing in production code and fake dependencies in test code. In the second case your test has to subclass the class that you want to mock and build the component from scrach

Categories

Resources