I don't know what's wrong with below problem?
Code:
CategoryDAO.kt
#Dao
interface CategoryDAO {
#Query("select * from CategoryDesign")
suspend fun getCategory(): MutableLiveData<List<CategoryDesign>>
}
CategoryDesign.kt
#Entity
data class CategoryDesign (
#PrimaryKey
var categoryDesignID:String,
var designImage:String,
var designTitle:String){
constructor() : this("","","")
override fun toString(): String {
return designTitle
}
}
Press Run App button will show error like below:
error: Not sure how to convert a Cursor to this method's return type (androidx.lifecycle.MutableLiveData<java.util.List<com.squall.searchdesigner.model.CategoryDesign>>).
public abstract java.lang.Object getCategory(#org.jetbrains.annotations.NotNull()
w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: androidx.room.RoomProcessor (DYNAMIC), android.databinding.annotationprocessor.ProcessDataBinding (DYNAMIC).
dependencies:
dependencies {
def room_version = "2.2.3"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
}
It's seems like you can use only LiveData return type. Maybe Room not allows you to use MutableLiveData?
Thank you all.
I used below code and rebuild project and invalidate caches seems solve this problem.
#Dao
interface CategoryDAO {
#Query("select * from CategoryDesign")
fun getCategory(): LiveData<MutableList<CategoryDesign>>
}
Related
I have a kotlin multiplatform project and I need to serialize a class. I have included the following dependency in my commonMain and androidMain and iosMain respectively in my multiplatform gradle file:
//commonMain
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:0.20.0"
//androidMain
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
//iosMain
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.20.0"
This is the class I want to serialize:
#Serializable
class Test {
fun toJsonTestClass() = Json.stringify(Test.serializer(), this)
var value1:Double = 0.toDouble()
companion object {
fun buildClass(value1 : Double):Test {
val test = Test()
test.value1 = value1
return test
}
fun fromJsonTestClass(json:String) = Json.parse(Test.serializer(), json)
}
}
And in another class (TrialClass), this is how I am testing it:
val test = Test()
val testToJson = test.toJsonTestClass() //<- This is where the error is pointing to.
println(testToJson)
But when I run it, I get the following error:
Exception in thread "main" java.lang.NoClassDefFoundError: kotlinx/serialization/json/Json
at Test.toJsonTestClass(Test.kt:28)
at TrialClass.main(TrialClass.kt:4)
Caused by: java.lang.ClassNotFoundException: kotlinx.serialization.json.Json
I get no issues when having the imports in my class, but when running I get the above mentioned error.
Any help or advice will be highly appreciated.
For my case, I did everything right - code & configuration. And gradle clean didn't help, I ended up with the magic Invalidate Caches / Restart from IntelliJ IDEA.
Your class is probably being obfuscated. You have two options:
Add the #Keep annotation above the class:
#Serializable
#Keep
class Teste {
// ...
}...
or add it to your module's proguard:
-keep (path_to_the_class).test.** { *; }
I'm trying to test the following LocalDataSource function, NameLocalData.methodThatFreezes function, but it freezes. How can I solve this? Or How can I test it in another way?
Class to be tested
class NameLocalData(private val roomDatabase: RoomDatabase) : NameLocalDataSource {
override suspend fun methodThatFreezes(someParameter: Something): Something {
roomDatabase.withTransaction {
try {
// calling room DAO methods here
} catch(e: SQLiteConstraintException) {
// ...
}
return something
}
}
}
Test class
#MediumTest
#RunWith(AndroidJUnit4::class)
class NameLocalDataTest {
private lateinit var nameLocalData: NameLocalData
// creates a Room database in memory
#get:Rule
var roomDatabaseRule = RoomDatabaseRule()
#get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
#Before
fun setup() = runBlockingTest {
initializesSomeData()
nameLocalData = NameLocalData(roomDatabaseRule.db)
}
#Test
fun methodThatFreezes() = runBlockingTest {
nameLocalData.methodThatFreezes // test freezes
}
// ... others NameLocalDataTest tests where those functions been tested does not use
// roomDatabase.withTransaction { }
}
Gradle's files configuration
espresso_version = '3.2.0'
kotlin_coroutines_version = '1.3.3'
room_version = '2.2.5'
test_arch_core_testing = '2.1.0'
test_ext_junit_version = '1.1.1'
test_roboletric = '4.3.1'
test_runner_version = '1.2.0'
androidTestImplementation "androidx.arch.core:core-testing:$test_arch_core_testing"
androidTestImplementation "androidx.test.espresso:espresso-core:$espresso_version"
androidTestImplementation "androidx.test.ext:junit:$test_ext_junit_version"
androidTestImplementation "androidx.test:rules:$test_runner_version"
androidTestImplementation "androidx.test:runner:$test_runner_version"
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$kotlin_coroutines_version"
Last time I wrote a test for Room database I just simply use runBlock and it worked for me...
Could you take a look into this sample and check if it works for you as well?
Edit:
Ops! I missed this part... I tried this (in the same sample):
I defined a dummy function in my DAO using #Transaction
#Transaction
suspend fun quickInsert(book: Book) {
save(book)
delete(book)
}
I think this is the key of the problem. Add setTransactionExecutor to your Database instantiation.
appDatabase = Room.inMemoryDatabaseBuilder(
InstrumentationRegistry.getInstrumentation().context,
AppDatabase::class.java
).setTransactionExecutor(Executors.newSingleThreadExecutor())
.build()
Finally, the test worked using runBlocking
#Test
fun dummyTest() = runBlocking {
val dao = appDatabase.bookDao();
val id = dummyBook.id
dao.quickInsert(dummyBook)
val book = dao.bookById(id).first()
assertNull(book)
}
See this question.
I had tried many things to make this work, used runBlockingTest, used TestCoroutineScope, tried runBlocking, used allowMainThreadQueries, setTransactionExecutor, and setQueryExecutor on my in memory database.
But nothing worked until I found this comment thread in the Threading models in Coroutines and Android SQLite API article in the Android Developers Medium blog, other people mentioned running into this. Author Daniel Santiago said:
I’m not sure what Robolectric might be doing under the hood that could cause withTransaction to never return.
We usually don’t have Robolectric tests but we have plenty of Android Test examples if you want to try that route: https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/room/integration-tests/kotlintestapp/src/androidTest/java/androidx/room/integration/kotlintestapp/test/SuspendingQueryTest.kt
I was able to fix my test by changing it from a Robolectric test to an AndroidTest and by using runBlocking
This is an example from the google source:
#Before
#Throws(Exception::class)
fun setUp() {
database = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
TestDatabase::class.java
)
.build()
booksDao = database.booksDao()
}
#Test
fun runSuspendingTransaction() {
runBlocking {
database.withTransaction {
booksDao.insertPublisherSuspend(
TestUtil.PUBLISHER.publisherId,
TestUtil.PUBLISHER.name
)
booksDao.insertBookSuspend(TestUtil.BOOK_1.copy(salesCnt = 0))
booksDao.insertBookSuspend(TestUtil.BOOK_2)
booksDao.deleteUnsoldBooks()
}
assertThat(booksDao.getBooksSuspend())
.isEqualTo(listOf(TestUtil.BOOK_2))
}
}
The error I have:
The code with the error:
#RunWith(PowerMockRunner::class)
#PrepareForTest(PotatoProvider::class, PotatoConsumer::class)
class WantedButNotInvoked {
#Mock
lateinit var potatoConsumer: PotatoConsumer
#Test
fun potato() {
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
//verify(potatoConsumer).accept(any()) //-> This fails too with the same reason
}
}
data class Potato(val value: Int = 1)
class PotatoConsumer : Consumer<Potato> {
override fun accept(t: Potato?) {
println(t)
}
}
So I making subscribe with this mock(potatoConsumer), and the rxJava have called 'accept', and mockito mark it as interaction, but mockito thinks this interaction is not what I'm expecting, why?
Versions of libraries used her:
mockitoVersion = '2.8.9'
mockitoAndroidVersion = '2.7.22'
powerMockVersion="2.0.2"
kotlinMockito="2.1.0"
rxKotlin = "2.3.0"
rxJavaVersion = "2.2.10"
Kinda workaround
Some fields mocked by powermock, fails on 'verify', but fields mocked with mockito is not;
Mockito can't mock not opened fields, without mock-maker-inline, but mockito conflicts with Powermock mock-maker-inline;
Powermock can delegate calls of mock-maker-inline to other mock-maker-inline(https://github.com/powermock/powermock/wiki/PowerMock-Configuration)
Use Mockito.mock on the failed fields instead of #Mock/Powermock mock injection
Example of the "green" potato test method using PowerMockRunner
#Test
fun potato() {
potatoConsumer = mock() // <-
Observable.just(Potato()).subscribe(potatoConsumer)
verify(potatoConsumer).accept(potato)
}
I am not familiar with PowerMock but I tried this test and it passes:
#Test
fun potato() {
fakePotatoProvider = Mockito.mock(PotatoProvider::class.java)
potatoConsumer = Mockito.mock(PotatoConsumer::class.java)
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(Potato()))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(Potato())
}
Maybe because you aren't passing the same instance of Potato(). Try to refactor your code to this
#Test
fun potato() {
val testPotato = Potato()
`when`(fakePotatoProvider.getObservable()).thenReturn(Observable.just(testPotato))
fakePotatoProvider.getObservable().subscribe(potatoConsumer)
verify(potatoConsumer).accept(testPotato)
}
As I mentioned above, the reason why it might be failing is the constant creation of new instances when passing your Potato object, hance that comparison fails.
So, I recently started experimentation with coroutines, I switched from Rxjava2 to coroutines, I haven't got a grasp of it yet but still, I ran into a condition where I needed to observe my database change and update the UI corresponding to that.
RxJava used to provide me with Flowables, Completeable etc. using that I would be able to observe changes in Db.
abstract fun insert(data: SomeData): Long
#Query("SELECT * FROM somedata_table")
abstract fun getData(): Flowable<List<SomeData>>
So here now I used to subscribe to getData and always used to observe changes
Now Enter coroutines, I am using a suspended function with a deferred result to return my responses
#Insert(onConflict = OnConflictStrategy.IGNORE)
abstract fun insert(data: SomeData): Long
#Query("SELECT * FROM somedata_table")
abstract fun getData(): List<SomeData>
suspend fun getAllSomeData():Deferred<List<SomeData>>{
return GlobalScope.async (context= coroutineContext){
database.myDao().getData()
}
}
Now I have no way to listen for updates, Channels in coroutines might be the right answer? but I am not sure how to use it with Room.
Use Room 2.2.0 Flows and kotlin coroutines. It's contentious but I dislike LiveData as it gives you results on the UI thread. If you have to do any data parsing you'll have to push everything back to another IO thread. It's also cleaner than using channels directly as you have to do extra openSubscription().consumeEach { .. } calls every time you want to listen to events.
Flow approach Requires the following versions:
// this version uses coroutines and flows in their non-experimental version
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2
androidx.room:room-runtime:2.2.0
androidx.room:room-compiler:2.2.0
Dao:
#Dao
interface MyDao {
#Query("SELECT * FROM somedata_table")
fun getData(): Flow<List<SomeData>>
}
class to do observation:
launch {
dao.getData().collect { data ->
//handle data here
}
}
if your calling class is not itself a CoroutineScope you'd have to call launch with the context of something that is. That can be GlobalScope or some other class you create. Here I'm using lifecycleScope assuming we're in an Activity class.
lifecycleScope.launch {
dao.getData().collect { data ->
//handle data here
}
}
the collect lambda will receive every udpate to the table much like an Rx onNext call.
Currently, there are two different ways of doing that. The first is to use a liveData builder function. To make this work, you need to update lifecycle to androidx.lifecycle:*:2.2.0-alpha01 or any newer version. The LiveData builder function will be used to call getData() asynchronously, and then use emit() to emit the result. Using this method, you will modify your Room getData() function to a suspend function and make the return type wrapped as a LiveData, replacing the Flowable used before.
#Query("SELECT * FROM somedata_table")
abstract suspend fun getData(): LiveData<List<SomeData>>
In your viewmodel you create a liveData which references your Room database
val someData: LiveData<SomeData> = liveData {
val data = database.myDao().getData()
emit(data)
}
The second approach is to get data from our DB as Flow. To use this, you need to update Room to androidx.room:room-*:2.2.0-alpha02 (currently the latest) or a newer version. This update enables #Query DAO methods to be of return type Flow The returned Flow will re-emit a new set of values if the observing tables in the query are invalidated. Declaring a DAO function with a Channel return type is an error
#Query("SELECT * FROM somedata_table")
abstract fun getData(): Flow<List<SomeData>?>
The return type is a flow of a nullable list. The list is nullable because Room will return null when the query has no data fetched.
To fetch data from the flow we will use the terminal operator collect{ } in our Presenter/ViewModel. It is preferable to do this in the ViewModel since it comes with a ViewModelScope. The solution given below assumes we are doing this in a ViewModel where we have a provided viewModelScope.
fun loadData(){
viewModelScope.launch {
database.myDao()
.getData()
.distinctUntilChanged().
.collect{
it?.let{ /** Update your obsevable data here **/
}
}
Gradle dependencies:
dependencies {
compile group: 'org.jetbrains.kotlinx', name: 'kotlinx-coroutines-reactive', version: '1.1.1'
}
Room Dao
#Dao
interface HistoryDao : BaseDao<HistoryEntity> {
#Query("select * from History order by time desc")
fun observe(): Flowable<List<HistoryEntity>>
...
}
Interactor (browserHistoryInteractor below) (layer between dao and Fragment/Presenter)
// To get channel of List<HistoryEntity>:
import kotlinx.coroutines.reactive.openSubscription
fun observe() = historyDao.observe().openSubscription() // convert list to Coroutines channel
Presenter/Fragment/Activity (end point (in my case it is lifecycle-aware presenter))
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
private val compositeJob = Job() // somewhat equivalent "compositeDisposable" in rx
override fun onCreate() {
super.onCreate()
launch(compositeJob) { // start coroutine
val channel = browserHistoryInteractor.observe()
for (items in channel) { // waits for next list of items (suspended)
showInView { view?.setItems(items) }
}
}
}
override fun onDestroy() {
compositeJob.cancel() // as in rx you need to cancel all jobs
super.onDestroy()
}
https://www.youtube.com/watch?v=lh2Vqt4DpHU&list=PLdb5m83JnoaBqMWF-qqhZY_01SNEhG5Qs&index=5 at 29:25
I'm trying to implement Dagger 2 in a test app to learn Clean Architecture and dependancy injection in Kotlin language.
EDIT :
I can compile thanks to #Logain, but I have always the static member problem with Dagger in my singleton (see below my TaskWorker), so I'm looking for how can I fix this error
But i got a problem, my DaggerComponent is well generated when i do a rebuild, but not when i want to run my app for testing, it fails and disappears. It fails with this error :
Error:(21, 29) Unresolved reference: DaggerInjectorComponent
Error:Execution failed for task ':app:compileDebugKotlinAfterJava'.
> Compilation error. See log for more details
While when i do a rebuild, this task is passed correctly
:app:compileDebugKotlinAfterJava
So i don't understand why it fails.
Here is my InjectorComponent :
#Singleton
#Component(modules = arrayOf(ContextDaggerModule::class, LocalStoreDaggerModule::class))
interface InjectorComponent {
fun inject(realmLocalStore: RealmLocalStore)
fun inject(taskWorker: TaskWorker)
}
ContectDaggerModule :
#Module
class ContextDaggerModule (val app: Application) {
#Provides
#Singleton
fun provideContext(): Context = app
#Provides
#Singleton
fun provideApplication(): Application = app
#Provides
#Singleton
fun provideResources(): Resources = app.resources
}
LocalStoreDaggerModule :
#Module
class LocalStoreDaggerModule {
#Provides
#Singleton
fun provideLocalStore(context: Context): LocalStore {
return RealmLocalStore(context)
}
}
I think the problem is caused because I inject dependencies in Object-declarations but all elements are static and Dagger does not appreciate it.
So, i try to hack it with a simple override getter and injecting data but nop.
Here is my "hack" :
object TaskWorker {
// #Inject lateinit var localStore: LocalStore
// Not work cause it's a static variable
var localStore: LocalStore? = null
#Inject
get() = localStore
// some cool function
}
I follow this code and this tutorial
I use these dependencies :
// Dagger2
compile 'com.google.dagger:dagger:2.11'
kapt 'com.google.dagger:dagger-compiler:2.11'
provided 'org.glassfish:javax.annotation:10.0-b28'
Make sure you are using:
kapt {
generateStubs = true
}
Due to some limitations on kapt
Or just try with:
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
You don't need this.
kapt {
generateStubs = true
}
Just apply the plugin:
apply plugin: 'kotlin-kapt'
and add the dependencies:
compile androidDependencies.dagger2
compile androidDependencies.dagger2Android
kapt androidDependencies.dagger2Kapt
sometimes the tasks fail with errors like that. Try to clean and as last resort use invalidate and restart. Most of the times it works.