I was wondering why I receive the error:
"java.lang.RuntimeException: exception while computing database live data."
I usually get this when running this in an androidTest:
#RunWith(AndroidJUnit4::class)
class ViewModelTest {
#get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
#Test
#Throws(Exception::class)
fun tester() = runBlocking {
ActivityScenario.launch(LoginActivity::class.java)
Thread.sleep(2000)
}
}
Could someone explain why I'm getting this error? I realize that I'm not using any live data at the moment, but was wondering why I'm getting this error in the first place.
I've used livedata on unit tests and I don't seem to receive this error. Only when I try launching an activity/scenario or do UI testing.
You might want to consider upgrading your sqlite database and your sqlite libraries.
Related
I'm trying to test the insert operation in my room database. I have written the following code.
#Test
fun test() = runTest {
val testString = "hello"
dao.insert(testString)
val allStrings = dao.getAllStrings()
assertThat(allStrings).contains(testString)
}
The problem is dao.getAllStrings() returns a flow i.e. Flow<List<String>> so assertThat function written here does not work. Does anyone know how to efficiently handle the Flow here and get the List<String> out from that so that I can apply the assertThat function. Thanks in advance :)
I'm trying to migrate my MVP application from Rx to Kotlin's coroutines (which I'm new to).
As I was running some trials, I found that any code with the following structure fails to compile with org.jetbrains.kotlin.codegen.CompilationException: Back-end (JVM) Internal error: wrong bytecode generated
val scope = CoroutineScope(Dispatchers.IO)
fun a(i: Int) {
scope.launch {
withContext(Dispatchers.Main) {
val b = i + 1
}
}
}
It appears that trying to access the parameter i inside the withContext is the problem. If I assign the value of i to something else inside the function block and use that instead, it works alright. But I have the feeling I might be doing something extra wrong here hehe
This lives in a Presenter. My idea is to use "launch" with the IO dispatcher to call the repository and get some data from the database, then use it to update the UI in the Main dispatcher. It looks solid to me, but I'm a bit worried because apparently no one else is running into that same issue, which might mean this pattern I'm trying to implement should be avoided for some reason.
Any clues?
After some fiddling I found that the problem was with my kotlin plugin version.
I changed it from 1.3.50 to 1.3.72 and... magic :D
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.)
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() {
....
}
Im writting Unit Tests for my app and I've found a "speed bump" while writting them. While testing subclasses of AndroidViewModel im missing the Application parameter for its initialization. I've already read this question that uses Robolectric.
This is what I already tried so far:
Using Robolectric as the question describes. As far as I understand, Robolectric can use your custom Application class for testing, the thing is im not using a custom application class as I dont need it. (the app is not that complex).
Using mockito. Mockito throws an exception saying that the Context class cannot be mocked.
Using the InstrumentationRegistry. I moved the test classes from the test folder to the androidTest folder, giving me access to the androidTestImplementation dependencies, I tried using the InstrumentationRegistry.getContext() and parse it to Application, of course this didn't worked, throwing a cast exception. I felt so dumb trying this but again, it was worth the shot.
I just want to instanciate my AndroidViewModel classes so I can call their public methods, but the Application parameter is needed. What can I do for this?
fun someTest() {
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(**missing application parameter**)
testViewModel.foo() // The code never reaches here as the testViewModel cant be initializated
}
I had the same issue, and found two solutions.
You can use Robolectric in a Unit Test, inside test directory, and choose the platform Application class.
#RunWith(RobolectricTestRunner::class)
#Config(application = Application::class)
class ViewModelTest {
#Test
#Throws(Exception::class)
fun someTest() {
val application = RuntimeEnvironment.application
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(application)
testViewModel.foo()
}
}
Or you can use an InstrumentationTest inside androidTest directory, and cast the InstrumentationRegistry.getTargetContext().applicationContext to Application:
#RunWith(AndroidJUnit4::class)
class ViewModelTest {
#Test
#Throws(Exception::class)
fun someTest() {
val application = ApplicationProvider.getApplicationContext() as Application
val testViewModel = MyViewModelThatExtendsFromAndroidViewModel(application)
testViewModel.foo()
}
}
Hope it helped!