kotlin testing error cannot create parent directories - kotlin

When I run test, I am getting the error when I try to test if Datastore.edit had been called:
java.io.IOException: Unable to create parent directories of /data/user/0/com.example.app.test/files/datastore/user.preferences_pb
It is a small class that handles Datastore, which is the following:
Pref.kt
class Pref {
companion object {
#Volatile
private var INSTANCE: Pref? = null
private var dataStore: DataStore<Preferences>? = null
fun getInstance(context: Context): Pref? {
if (INSTANCE == null) {
synchronized(Pref::class) {
INSTANCE = Pref()
dataStore = context.createDataStore("user")
}
}
return INSTANCE
}
}
suspend fun save(key: String, value: String) {
val dataStoreKey = preferencesKey<String>(key)
dataStore?.edit { settings ->
settings[dataStoreKey] = value
}
}
}
PrefTest.kt
import android.content.Context
import android.provider.ContactsContract
import android.util.Log
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.datastore.core.DataStore
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.*
import org.junit.Rule
import org.junit.runner.RunWith
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.preferencesKey
import androidx.test.platform.app.InstrumentationRegistry
import io.mockk.*
import io.mockk.MockKAnnotations.init
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
#RunWith(AndroidJUnit4::class)
class PrefTest {
private val context : Context = InstrumentationRegistry.getInstrumentation().context
#get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
private val mockDataStore = mockk<DataStore<Preferences>>()
#Before
fun setup() {
init(this, relaxed = true)
}
#Test
fun saveValue() = runBlocking {
val dataStoreKey = preferencesKey<String>(storeKey)
Pref.getInstance(context)?.save("key", "value")
coVerify { mockDataStore.edit {
dataStoreKey
} }
}
}
in my device, I can see the directory /data/user/0/com.example.app.test/files. But not sure why the datastore directories couldn't be created for it to continue testing.

Related

Error while accessing references from Resource.kt file in Kotlin project

I get below error while trying to access refferences from my Resources.kt file
.
Below shows my full code on LoginViewModel.kt
package lk.ac.kln.mit.stu.mobileapplicationdevelopment.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bumptech.glide.load.engine.Resource
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.firestore.auth.User
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
#HiltViewModel
class LoginViewModel #Inject constructor(
private val firebaseAuth: FirebaseAuth
): ViewModel() {
private val _login = MutableSharedFlow<Resource<FirebaseUser>>()
// MutableSharedFlow<Resource<FirebaseUser>>()
val login = _login.asSharedFlow()
fun login(email: String, password: String){
viewModelScope.launch {
_login.emit(Resource.Loading())
}
firebaseAuth.signInWithEmailAndPassword(
email,password
).addOnSuccessListener {
viewModelScope.launch {
it.user?.let {
_login.emit(Resource.Success(it))
}
}
}.addOnFailureListener {
viewModelScope.launch {
_login.emit(Resource.Error(it.message.toString()))
}
}
}
}
Below shows the Resource.kt file
package lk.ac.kln.mit.stu.mobileapplicationdevelopment.util
sealed class Resource<T>(
val data: T? = null,
val message: String? = null
){
class Success<T>(data:T):Resource<T>(data)
class Error<T>(message:String):Resource<T>(message = message)
class Loading <T>: Resource<T>()
class Unspecified <T>: Resource<T>()
}
I have tried to implement using the same way I did for other view models.
I did the same approach to access same variables on RegisterViewModel.kt and it works without any error. Below is the code for that functioning view model
package lk.ac.kln.mit.stu.mobileapplicationdevelopment.viewmodel
import androidx.lifecycle.ViewModel
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.firestore.FirebaseFirestore
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.runBlocking
import lk.ac.kln.mit.stu.mobileapplicationdevelopment.data.User
import lk.ac.kln.mit.stu.mobileapplicationdevelopment.util.*
import lk.ac.kln.mit.stu.mobileapplicationdevelopment.util.Constants.USER_COLLECTION
import javax.inject.Inject
#HiltViewModel
class RegisterViewModel #Inject constructor(
private val firebaseAuth : FirebaseAuth,
private val db: FirebaseFirestore
) : ViewModel() {
private val _register = MutableStateFlow<Resource<User>>(Resource.Unspecified())
val register : Flow<Resource<User>> = _register
private val _validation = Channel<RegisterFieldsState>()
val validation =_validation.receiveAsFlow()
fun createAccountWithEmailAndPassword(user: User, password: String) {
if (checkValidation(user, password)){
runBlocking {
_register.emit(Resource.Loading())
}
firebaseAuth.createUserWithEmailAndPassword(user.email, password)
.addOnSuccessListener {
it.user?.let {
saveUserInfo(it.uid, user)
// _register.value = Resource.Success(it)
}
}
.addOnFailureListener {
_register.value = Resource.Error(it.message.toString())
}
} else
{
val registerFieldsState = RegisterFieldsState(
validateEmail(user.email), validatePassword(password)
)
runBlocking {
_validation.send(registerFieldsState)
}
}
}
private fun saveUserInfo(userUid: String, user: User) {
db.collection(USER_COLLECTION)
.document(userUid)
.set(user)
.addOnSuccessListener {
_register.value = Resource.Success(user)
}
.addOnFailureListener {
_register.value = Resource.Error(it.message.toString())
}
}
private fun checkValidation(
user: User,
password: String
) : Boolean {
val emailValidation = validateEmail(user.email)
val passwordValidation = validatePassword(password)
val shouldRegister =
emailValidation is RegisterValidation.Success && passwordValidation is RegisterValidation.Success
return shouldRegister
}
}
Can someone help me with solving above issue please.Why it gives an error when trying to call Resource.Loading() from LoginViewModel.kt?
Please check import class in LoginViewModel, You are importing
import com.bumptech.glide.load.engine.Resource
It should be from this package lk.ac.kln.mit.stu.mobileapplicationdevelopment.util

TransactionalEventListener not invoked in #MicronautTest

Problem description
While system end-to-end tests are invoking methods annotated with #TransactionalEventListener, I'm not able to invoke the same methods in narrower tests annotated with #MicronautTest.
I've tested numerous variants with both injected EntityManager and SessionFactory. #MicronautTest(transactional = false) is also tested. Calling JPA-method inside TestSvcWithTxMethod#someMethod is also tested with same result. I've also tried tests without mocking TestAppEventListener.
The below test/code yields
Verification failed: call 1 of 1:
TestAppEventListener(#1).beforeCommit(any())) was not called.
java.lang.AssertionError: Verification failed: call 1 of 1:
TestAppEventListener(#1).beforeCommit(any())) was not called.
Calls to same mock: 1) TestAppEventListener(#1).hashCode()
Environment: Micronaut 3.7.5, Micronaut Data 3.9.3
Minimal reproducible code
Test is failing as well with transactional = false
import io.kotest.core.spec.style.BehaviorSpec
import io.micronaut.test.annotation.MockBean
import io.micronaut.test.extensions.kotest5.MicronautKotest5Extension.getMock
import io.micronaut.test.extensions.kotest5.annotation.MicronautTest
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import no.mycompany.myapp.eventstore.services.appeventpublisher.testinfra.DefaultTestAppEventListener
import no.mycompany.myapp.eventstore.services.appeventpublisher.testinfra.TestAppEventListener
import no.mycompany.myapp.eventstore.services.appeventpublisher.testinfra.TestSvcWrapper
#MicronautTest
class AppEventWithBeforeCommitListenerMockTest(
testSvcWrapper: TestSvcWrapper,
testAppEventListener: TestAppEventListener
) : BehaviorSpec({
given("context with app event listener") {
`when`("calling someMethod") {
val mockBeforeCommitTestListener = getMock(testAppEventListener)
every { mockBeforeCommitTestListener.beforeCommit(any()) } answers {}
every { mockBeforeCommitTestListener.afterRollback(any()) } answers {}
testSvcWrapper.someMethod(message = "call #1")
verify { mockBeforeCommitTestListener.beforeCommit(any()) }
}
}
}) {
#MockBean(DefaultTestAppEventListener::class)
fun mockTestAppEventListener(): TestAppEventListener = mockk()
}
TestSvcWrapper
import jakarta.inject.Singleton
#Singleton
class TestSvcWrapper(
private val testSvcWithTxMethod: TestSvcWithTxMethod
) {
fun someMethod(message: String) {
testSvcWithTxMethod.someMethod(message)
}
}
TestSvcWithTxMethod
import io.micronaut.context.event.ApplicationEventPublisher
import jakarta.inject.Singleton
import javax.transaction.Transactional
#Singleton
open class TestSvcWithTxMethod(
private val eventPublisher: ApplicationEventPublisher<TestEvent>
) {
#Transactional(Transactional.TxType.REQUIRES_NEW)
open fun someMethod(message: String) {
eventPublisher.publishEvent(TestEvent(message))
}
}
TestEvent
import io.micronaut.core.annotation.Introspected
#Introspected
data class TestEvent(val message: String)
TestAppEventListener
interface TestAppEventListener {
fun beforeCommit(event: TestEvent)
fun afterRollback(event: TestEvent)
}
DefaultTestAppEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import jakarta.inject.Singleton
import java.util.concurrent.atomic.AtomicInteger
#Singleton
open class DefaultTestAppEventListener : TestAppEventListener {
val receiveCount = AtomicInteger()
#TransactionalEventListener(TransactionalEventListener.TransactionPhase.BEFORE_COMMIT)
override fun beforeCommit(event: TestEvent) {
receiveCount.getAndIncrement()
}
#TransactionalEventListener(TransactionalEventListener.TransactionPhase.AFTER_ROLLBACK)
override fun afterRollback(event: TestEvent) {
receiveCount.getAndIncrement()
}
}
The answer was found in the micronaut-test repo. Key is to inject SynchronousTransactionManager<Any>, create and then commit/rollback transaction.
I was not able to make mock-test from question pass, most likely because of the annotations, but the following tests are working. I made some modifications to the types in question, hence I added code for the new implementations below.
import io.kotest.core.spec.style.BehaviorSpec
import io.kotest.matchers.shouldBe
import io.micronaut.test.extensions.kotest5.annotation.MicronautTest
import io.micronaut.transaction.SynchronousTransactionManager
import io.micronaut.transaction.support.DefaultTransactionDefinition
import no.mycompany.myapp.eventstore.services.appeventpublisher.testinfra.TestAppEventListener
import no.mycompany.myapp.eventstore.services.appeventpublisher.testinfra.TestSvcWithTxMethod
#MicronautTest(transactional = false)
class AppEventWithBeforeCommitListenerTest(
testSvcWithTxMethod: TestSvcWithTxMethod,
testAppEventListener: TestAppEventListener,
transactionManager: SynchronousTransactionManager<Any>
) : BehaviorSpec({
given("context with app event listener") {
`when`("calling someMethod with commit") {
val tx = transactionManager.getTransaction(DefaultTransactionDefinition())
testSvcWithTxMethod.someMethod(message = "call #1")
transactionManager.commit(tx)
then("TestAppEventListener should have received message") {
testAppEventListener.beforeCommitReceiveCount.get() shouldBe 1
}
}
`when`("calling someMethod with rollback") {
val tx = transactionManager.getTransaction(DefaultTransactionDefinition())
testSvcWithTxMethod.someMethod(message = "call #2")
transactionManager.rollback(tx)
then("TestAppEventListener should have received message") {
testAppEventListener.afterRollbackReceiveCount.get() shouldBe 1
}
}
}
})
TestSvcWithTxMethod
import io.micronaut.context.event.ApplicationEventPublisher
import jakarta.inject.Singleton
import javax.transaction.Transactional
#Singleton
open class TestSvcWithTxMethod(
private val eventPublisher: ApplicationEventPublisher<TestEvent>
) {
#Transactional
open fun someMethod(message: String) {
eventPublisher.publishEvent(TestEvent(message))
}
}
TestAppEventListener
import io.micronaut.transaction.annotation.TransactionalEventListener
import jakarta.inject.Singleton
import java.util.concurrent.atomic.AtomicInteger
#Singleton
open class TestAppEventListener {
val beforeCommitReceiveCount = AtomicInteger()
val afterRollbackReceiveCount = AtomicInteger()
#TransactionalEventListener(TransactionalEventListener.TransactionPhase.BEFORE_COMMIT)
open fun beforeCommit(event: TestEvent) {
beforeCommitReceiveCount.getAndIncrement()
}
#TransactionalEventListener(TransactionalEventListener.TransactionPhase.AFTER_ROLLBACK)
open fun afterRollback(event: TestEvent) {
afterRollbackReceiveCount.getAndIncrement()
}
}
TestEvent (unchanged)
import io.micronaut.core.annotation.Introspected
#Introspected
data class TestEvent(val message: String)

Kotlin Micronaut Mockk no answer found exception

I am new to the Micronaut framework and trying to test a demo example I created.
I am encountering the error io.mockk.MockKException: no answer found for: MathFacade(#3).computeAgain(3).
My code works as such - MathService invokes MathFacade. I want to test MathService so I mock MathFacade in my MathServiceTest with intentions to only test MathService.
My code is as below:
MathServiceTest
package io.micronaut.test.kotlintest
import io.kotlintest.specs.BehaviorSpec
import io.micronaut.test.annotation.MicronautTest
import io.micronaut.test.annotation.MockBean
import io.micronaut.test.extensions.kotlintest.MicronautKotlinTestExtension.getMock
import io.mockk.mockk
import io.mockk.every
import io.reactivex.Single
import kotlin.math.pow
import kotlin.math.roundToInt
#MicronautTest
class MathServiceTest(
private val mathService: MathService,
private val mathFacade: MathFacade
): BehaviorSpec({
given("the math service") {
`when`("the service is called with 3") {
val mock = getMock(mathFacade)
every { mock.computeAgain(any()) } answers {
Single.just(firstArg<Int>().toDouble().pow(3).roundToInt())
}
val result = mathService.compute(2)
then("the result is 9") {
mathService.compute(3).test().assertResult(27)
}
}
}
}) {
#MockBean(MathFacade::class)
fun mathFacade(): MathFacade {
return mockk()
}
}
MathService
package io.micronaut.test.kotlintest
import io.reactivex.Single
import javax.inject.Singleton
#Singleton
class MathService(private val mathFacade: MathFacade) {
fun compute(num: Int): Single<Int> {
return mathFacade.computeAgain(num)
}
}
MathFacade
package io.micronaut.test.kotlintest
import io.reactivex.Single
import javax.inject.Singleton
#Singleton
class MathFacade() {
fun computeAgain(num: Int): Single<Int>{
return Single.just(num*4)
}
}
Any help will be appreciated!

Tests fail when run together but succeed individually even when instances are re-mocked before each test

I've looked at this SO post and made sure my tests don't share the same mocked instances, but the tests still fail when run together but succeed when run individually.
I suspect there might be something that prevents my instances from being re-mocked after every test, but I'm not experienced enough with Mockito to identify the issue
Here is my test class
package com.relic.viewmodel
import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.arch.lifecycle.Observer
import com.nhaarman.mockitokotlin2.*
import com.relic.api.response.Data
import com.relic.api.response.Listing
import com.relic.data.PostRepository
import com.relic.data.UserRepository
import com.relic.data.gateway.PostGateway
import com.relic.domain.models.ListingItem
import com.relic.domain.models.UserModel
import com.relic.presentation.displayuser.DisplayUserVM
import com.relic.presentation.displayuser.ErrorData
import com.relic.presentation.displayuser.UserTab
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.newSingleThreadContext
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.setMain
import org.junit.Before
import org.junit.Rule
import org.junit.Test
#ExperimentalCoroutinesApi
class UserVMTest {
#get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var postRepo : PostRepository
private lateinit var userRepo : UserRepository
private lateinit var postGateway : PostGateway
private val username = "testUsername"
init {
val mainThreadSurrogate = newSingleThreadContext("Test thread")
Dispatchers.setMain(mainThreadSurrogate)
}
#Before
fun setup() {
postRepo = mock()
userRepo = mock()
postGateway = mock()
}
#Test
fun `user retrieved on init`() = runBlocking {
val mockUser = mock<UserModel>()
whenever(userRepo.retrieveUser(username)).doReturn(mockUser)
val vm = DisplayUserVM(postRepo, userRepo, postGateway, username)
val observer : Observer<UserModel> = mock()
vm.userLiveData.observeForever(observer)
verify(userRepo, times(1)).retrieveUser(username)
verify(observer).onChanged(mockUser)
}
#Test
fun `livedata updated when posts retrieved` () = runBlocking {
val mockListingItems = listOf<ListingItem>(mock())
val listing = mockListing(mockListingItems)
whenever(postRepo.retrieveUserListing(any(), any(), any())).doReturn(listing)
val tab = UserTab.Saved
val vm = DisplayUserVM(postRepo, userRepo, postGateway, username)
val observer : Observer<List<ListingItem>> = mock()
vm.getTabPostsLiveData(tab).observeForever(observer)
vm.requestPosts(tab, true)
verify(postRepo, times(1)).retrieveUserListing(any(), any(), any())
verify(observer, times(1)).onChanged(mockListingItems)
}
#Test
fun `error livedata updated when no posts retrieved` () = runBlocking {
val listing = mockListing()
val localPostRepo = postRepo
whenever(localPostRepo.retrieveUserListing(any(), any(), any())).doReturn(listing)
val tab = UserTab.Saved
val vm = DisplayUserVM(postRepo, userRepo, postGateway, username)
val listingObserver : Observer<List<ListingItem>> = mock()
vm.getTabPostsLiveData(tab).observeForever(listingObserver)
val errorObserver : Observer<ErrorData> = mock()
vm.errorLiveData.observeForever(errorObserver)
vm.requestPosts(tab, true)
verify(postRepo, times(1)).retrieveUserListing(any(), any(), any())
// listing livedata shouldn't be updated, but an "error" should be posted
verify(listingObserver, never()).onChanged(any())
verify(errorObserver, times(1)).onChanged(ErrorData.NoMorePosts(tab))
}
private fun mockListing(
listingItems : List<ListingItem> = emptyList()
) : Listing<ListingItem> {
val data = Data<ListingItem>().apply {
children = listingItems
}
return Listing(kind = "", data = data)
}
}
Ok so after a bit more reading, it I've updated my code so that it uses the TestCoroutineDispatcher instead of newSingleThreadContext(). I also added a teardown method per the instructions here https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/
#After
fun teardown() {
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
mainThreadSurrogate.cleanupTestCoroutines()
}
Tests run successfully now

NoClassDefFoundError on using com.ning.http.client.AsyncHandler

I am building a receiver for Apache Spark after a template of szelvenskiythat gets an http-URL as input. I can package a jar with sbt, but on runtime the application throws a java.lang.NoClassDefFoundError: com.ning.http.client.AsyncHandler exception. Please find the code to implement the receiver here:
import org.apache.spark.streaming.receiver.Receiver
import org.apache.spark.storage.StorageLevel
import org.apache.spark.Logging
import com.ning.http.client.AsyncHttpClientConfig
import com.ning.http.client.AsyncHandler
import com.ning.http.client.AsyncHttpClient
import com.ning.http.client._
import scala.collection.mutable.ArrayBuffer
import java.io.OutputStream
import java.io.ByteArrayInputStream
import java.io.InputStreamReader
import java.io.BufferedReader
import java.io.InputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
class ParkingReceiver(url: String) extends Receiver[String](StorageLevel.MEMORY_AND_DISK_2) with Logging {
#transient var client: AsyncHttpClient = _
#transient var inputPipe: PipedInputStream = _
#transient var outputPipe: PipedOutputStream = _
def onStart() {
val cf = new AsyncHttpClientConfig.Builder()
cf.setRequestTimeout(Integer.MAX_VALUE)
cf.setReadTimeout(Integer.MAX_VALUE)
cf.setPooledConnectionIdleTimeout(Integer.MAX_VALUE)
client= new AsyncHttpClient(cf.build())
inputPipe = new PipedInputStream(1024 * 1024)
outputPipe = new PipedOutputStream(inputPipe)
val producerThread = new Thread(new DataConsumer(inputPipe))
producerThread.start()
client.prepareGet(url).execute(new AsyncHandler[Unit]() {
def onBodyPartReceived(bodyPart: HttpResponseBodyPart) = {
bodyPart.writeTo(outputPipe)
AsyncHandler.STATE.CONTINUE
}
def onStatusReceived(status: HttpResponseStatus) = {
AsyncHandler.STATE.CONTINUE
}
def onHeadersReceived(headers: HttpResponseHeaders) = {
AsyncHandler.STATE.CONTINUE
}
def onCompleted = {
println("completed")
}
def onThrowable(t: Throwable)={
t.printStackTrace()
}
})
}
def onStop() {
if (Option(client).isDefined) client.close()
if (Option(outputPipe).isDefined) {
outputPipe.flush()
outputPipe.close()
}
if (Option(inputPipe).isDefined) {
inputPipe.close()
}
}
class DataConsumer(inputStream: InputStream) extends Runnable
{
override
def run()
{
val bufferedReader = new BufferedReader( new InputStreamReader( inputStream ))
var input=bufferedReader.readLine()
while(input!=null){
store(input)
input=bufferedReader.readLine()
}
}
}
}
Have any of you experienced this before or may know what is missing in the code?