How to call function from fragment when use LaunchFragmentInContainer with Espresso - Android - android

In fragment I have a function that I want to call from my test. I made it public and visible for testing
#VisibleForTesting
fun myFun() {
//some code
}
So how can I call this function from my test
#Test
fun my_test(): Unit = runBlocking {
launchFragmentInContainer<MyDialog> {
//how to call myFun() from here <----
// this... does not show myFun()
}
}

Related

Testing coroutines in the presenter class

I'm struggling to test my presenter which is calling a suspended function from the repository layer as follow:
override fun viewCreated() {
launch {
val hasPermission = permissionChecker.execute() //suspended function
if (hasPermission) {
foo()
} else {
view.bar()
}
}
The presenter is also extending this interface:
interface CoroutinePresenter: CoroutineScope {
val job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
fun stopAllActiveJobs() {
coroutineContext.cancelChildren()
}
And the suspended function is defined as follow:
suspend fun execute() : Boolean = withContext(Dispatchers.IO) {
return#withContext class.foo()
}
Everything is working as expected in the app but when I tried to write some unit test I noticed that whenever I call the piece of code inside launch the thread is switched but the test doesn't wait for the execution. This is the implementation of the test:
#Test
fun `Test of Suspended Function`() = runBlocking {
presenter.viewCreated()
then(view).should().bar()
...
}
I also added the suggested library for testing kotlinx-coroutines-test but the result is still the same with it. I also tried to follow this suggestion and also implementing something like this but still no luck.
I think the problem is the actual creation of another thread whenever the launch is invoked in the presenter and the test doesn't actually know how to wait for it. I also tried to return a Job and invoking the job.join() but it fails with a NullPointerException.
Hope you guys can help me.
I found a solution for that:
following this tutorial, I've setup both
#Before
fun setup() {
Dispatchers.setMain(Dispatchers.Unconfined)
...
}
#After
fun tearDown() {
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
}
And by running the entire launch block of the presenter class inside a runBlocking statement in the test. The problem was related also to a not reported exception inside the suspended function that was actually not mocked but it was invisible to my eyes.
Now everything is working fine.
Firstly, I strongly recommend that give your coroutineContext as a Parameter like that:
class CoroutinePresenter(coroutineContext: CoroutineContext): CoroutineScope {
init{
_coroutineContext = coroutineContext
}
override val coroutineContext: CoroutineContext
get() = _coroutineContext
// Your Methods
}
In your real environment:
#YourScope
#Provides
fun providesCoroutinePresenter(coroutineContext:CoroutineContext ){
return CoroutinePresenter()
}
#YourScope
#Provides
fun providesCoroutineContext(){
return Dispatchers.Main + job
}
During the unit test:
#Before
fun setUp() {
coroutinePresenter CoroutinePresenter(Dispatchers.Unconfined)
}
#Test
fun `Should do something`(){
//WHEN
coroutinePresenter.doSomething(params)
//THEN
do your assertions
}
For more please check SOLID Principles and for this case D

How to mock android room withTransaction method with Mockk

I'm trying to make some unit tests for my business logic.
I have repository in which I save to room database (2.1.0-rc01) some data from response.
Data saving into different tables with different dao in single transaction.
Code is simplified:
ItemRepository
suspend fun saveItems(response: Response) {
val items = response.items.map { it.toLocalItem() }
val subItems = response.items.flatMap { item ->
item.subItems.map { it.toLocal(item.id) }
}
db.withTransaction {
db.itemDao().deleteAll()
db.itemDao().insertAll(items)
db.subItemDao().insertAll(subItems)
}
}
For unit test I'm using Mockk library. How can I mock room withTransaction method?. withTransaction is declared as
suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R
I'm trying to writing test
#MockK
private lateinit var database: AppDatabase
#MockK
private lateinit var itemDao: ItemDao
#MockK
private lateinit var subItemDao: SubItemDao
#Test
fun checkSaveItems() = runBlocking {
repository = ItemRepository(database)
coEvery { database.itemDao() } returns itemDao
coEvery { database.subItemDao() } returns subItemDao
//TODO: execute database.withTransaction(block: suspend () -> R)
coEvery { itemDao.deleteAll() } just Runs
coEvery { itemDao.insertAll(any()) } just Runs
coEvery { subItemDao.insertAll(any()) } just Runs
repository.saveItems(testResponse)
coVerifySequence {
itemDao.deleteAll()
itemDao.insertAll(testItems)
subItemDao.insertAll(testSubItems)
}
}
You first have to enable static mocks for the Android Room KTX method withTransaction {}. You also need to capture the suspend lambda function passed to it. This captured function can just be invoked so the code inside it runs. Since you're mocking all the database calls, you don't need a real transaction here.
#Before
fun initMocks() {
MockKAnnotations.init(this)
mockkStatic(
"androidx.room.RoomDatabaseKt"
)
val transactionLambda = slot<suspend () -> R>()
coEvery { db.withTransaction(capture(transactionLambda)) } coAnswers {
transactionLambda.captured.invoke()
}
}
You should then be able to run your code as written.
To expand on Andrew's answer, the mockk documentation for extension functions shows that if you are mocking an object wide or class wide extension function, you can just use regular mockk to achieve that. However, if you are using a module wide extension function, like withTransaction, you also need to perform mockkStatic on the module's class name.

Mock unit testing for RxJava

I'm writing unit tests base on Google's samples: TaskDetailPresenterTest.kt#L102
They use ArgumentCaptor<TasksDataSource.GetTaskCallback> to trigger callback with fake data COMPLETED_TASK
#Test
fun getCompletedTaskFromRepositoryAndLoadIntoView() {
presenter = TaskDetailPresenter(COMPLETED_TASK.id, tasksRepository, taskDetailView)
presenter.start()
// Then task is loaded from model, callback is captured
verify(tasksRepository).getTask(
eq(COMPLETED_TASK.id), capture(taskCallbackCaptor))
// When task is finally loaded
taskCallbackCaptor.value.onTaskLoaded(COMPLETED_TASK) // Trigger callback
}
Everything work fine because they use TasksDataSource.GetTaskCallback to return data. See: TaskDetailPresenter.kt#L36:
fun getTask(taskId: String, callback: GetTaskCallback)
Then use as
tasksRepository.getTask(taskId, object : TasksDataSource.GetTaskCallback {
override fun onTaskLoaded(task: Task) {
showTask(task)
}
}
But when I try to use RxJava Single<> instead of normal callback, like:
fun getTask(taskId: String): Single<Task>
Then use as
tasksRepository.getTask(taskId)
.subscribe(object : SingleObserver<Task> {
override fun onSuccess(task: Task) {
showTask(task)
}
override fun onError(e: Throwable) {
}
})
}
Then I cannot use ArgumentCaptor<> to trigger return fake data. It always throw NullPointerException when I execute my test, because tasksRepository.getTask(taskId) is always return null.
So how can I achieve the same unit test like Google did, but in RxJava?
My unit test code:
#Mock private lateinit var tasksRepository: TasksRepository
#Captor private lateinit var taskCaptor: ArgumentCaptor<SingleObserver<Task>>
private lateinit var presenter: TaskDetailPresenter
#Before fun setup() {
MockitoAnnotations.initMocks(this)
}
#Test
fun getCompletedTaskFromRepositoryAndLoadIntoView() {
presenter = TaskDetailPresenter(COMPLETED_TASK.id, tasksRepository, taskDetailView)
presenter.start()
// Then task is loaded from model, callback is captured
verify(tasksRepository).getTask(
eq(COMPLETED_TASK.id)).subscribe(taskCaptor.capture())
// When task is finally loaded
taskCaptor.value.onSuccess(COMPLETED_TASK) // Trigger callback
}
Note that all other parts (declare, setup, mocking,..) is the same as Google.
I don't know if you have already used this library but I would suggest you to use the Dagger 2 library with a MVP code architecture to ease your unit tests by improving your dependencies and couplings
All this method is doing is showTask(task: Task). So assert that this method is called after your observer starts observing. You shouldn't care what the showTask is going to do once it's called. If you use Rx it is much better to make your methods take arguments and return value of the observe pattern to make it easier write unit test.

Kotlin syntax for LiveData observer?

I have the following bit of code in my HomeActivity to use LiveData.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Show the launch splash screen.
//
this.setContentView(R.layout.activity_home)
this.viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
this.viewModel.getUser().observe(this, Observer { user: User? ->
});
}
While this seems to work, what does the following part mean?
Observer { user: User? ->
}
This must result in an object that conforms to the Observer interface which has
void onChanged (T t)
https://developer.android.com/reference/android/arch/lifecycle/Observer.html
How does
Observer { user: User? ->
}
result in an object with an onChanged method?
I don't know what putting the name of an interface in front of a lambda expression means.
Thanks!
This is called SAM Conversion, a concept that helps interacting with Java Single Abstract Method Interfaces like in your example.
The following creates an implementation of Runnable, where the single abstract method is run():
val runnable = Runnable { println("This runs in a runnable") }
It’s described in the docs: https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
Alternatively, but more verbose, would be to use an object:
val runnable2 = object : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
Both are examples of anonymous implementations of that interface. It's of course also possible to create a concrete subclass and instantiate it then.
class MyRunnable : Runnable {
override fun run() {
println("This runs in a runnable")
}
}
val runnable3 = MyRunnable()
in Kotlin the Observer { } lambda gives you param it, you can rename it as you want and use. by default data will be available with it.something() etc...
JAVA:
... new Observer {
void onChanged(User user){
user.something()
}
}
KOTLIN
... object : Observer<User> {
fun onChanged(user: User){
user.something()
}
}
OR
... Observer {
it.something()
}
you can rename it to whatever you want like
... Observer { myUser ->
myUser.something()
}
To omit the Observer { ... } part just add import androidx.lifecycle.observe and use it like this:
this.viewModel.user.observe(this) { user: User? ->
// ...
}

Pass interface as parameter in Kotlin

I want to pass an interface as parameter like this:
class Test {
fun main() {
test({})
// how can I pass here?
}
fun test(handler: Handler) {
// do something
}
interface Handler {
fun onCompleted()
}
}
In Java, I can use anonymous function like test(new Handler() { .......... }), but I can't do this in Kotlin. Anyone know how to do this?
In Kotlin you can do :
test(object: Handler {
override fun onComplete() {
}
})
Or make a property the same way:
val handler = object: Handler {
override fun onComplete() {
}
}
And, somewhere in code:
test(handler)
since your interface has only one function. you can convert it to SAM like this
fun interface Handler {
fun onCompleted()
}
then you can just implement this interface using lambda instead and so reduce the overall written code. this is only possible in v1.4
Attached is an example of how to pass an object by parameter that represents the value of the data type and invoke behaviors using interface inheritance.
fun main() {
val customClass = CustomClass(
object : First {
override fun first() {
super.first()
println("first new impl")
}
override fun second() {
super.second()
println("second new impl")
}
}
)
customClass.first.first()
customClass.first.second()
}
data class CustomClass(val first: First)
interface First: Second {
fun first() {
println("first default impl")
}
}
interface Second {
fun second() {
println("second default impl")
}
}
It is worth mentioning that with super.first() or super.second() the default behavior of the interface is being invoked.
It doesn't make much sense to pass a lamda with an anonymous object as a parameter, lambda: () -> Unit , if what we need is to invoke the functions.
GL
Playground

Categories

Resources