Kotlin and Android Espresso tests: Adding extension function with receiver - android

I'm still working on improving my understanding of extension functions with receivers and need some help of you experts on a question I have regarding this.
I have an Android Espresso testcase where I check that I have selected the items of a recyclerview. This is the same code repeated many times. I was wondering if it would be possible using kotlins extension functions with receiver to simplify this.
My test code now:
#Test
public void shouldSelectAll() {
...
onView(withRecyclerView(R.id.multiselectview_recycler_view).atPosition(0))
.check(RecyclerViewMatcher.isSelected(true));
onView(withRecyclerView(R.id.multiselectview_recycler_view).atPosition(1))
.check(RecyclerViewMatcher.isSelected(true));
onView(withRecyclerView(R.id.multiselectview_recycler_view).atPosition(2))
.check(RecyclerViewMatcher.isSelected(true));
}
Is it some how possible to create a function atPositions(varag positions: Int) that would take an integer array and call the assertion on each of the positions in the array. Like this:
#Test
public void shouldSelectAll() {
...
onView(withRecyclerView(R.id.multiselectview_recycler_view).atPositions(0, 1, 2))
.check(RecyclerViewMatcher.isSelected(true));
}

Sure!
private fun Int.matchAsRecyclerView(): RecyclerViewMatcher = withRecyclerView(this)
private fun RecyclerViewMatcher.checkAtPositions(vararg indices: Int, assertionForIndex: (Int) -> ViewAssertion) {
for(index in indices) {
onView(this.atPosition(index)).let { viewMatcher ->
viewMatcher.check(assertionForIndex(index))
}
}
}
Which should work as
R.id.multiselectview_recycler_view.matchAsRecyclerView().checkAtPositions(0, 1, 2, assertionForIndex = {
index -> RecyclerViewMatcher.isSelected(true)
})

Related

Mockk Mocking Private Properties in Kotlin

I have a simple class with a private field.
class EmployeeData {
private var employeeAge: Int = 0
fun getAge(): Int {
return 1 + employeeAge
}
}
I am trying to test this private employeeAge with the following from official docs
#Test
fun testPrivateAge() {
val mock = spyk(EmployeeData())
every {
mock getProperty "employeeAge"
} propertyType Int::class answers { fieldValue + 6 }
every {
mock setProperty "employeeAge" value any<Int>()
} propertyType Int::class answers { fieldValue += value }
every { mock getProperty "employeeAge" } returns 33
every { mock setProperty "employeeAge" value less(5) } just Runs
assertEquals(10,mock.getAge())
}
I am receiving such exception from MockK
io.mockk.MockKException: Missing calls inside every { ... } block.
at io.mockk.impl.recording.states.StubbingState.checkMissingCalls(StubbingState.kt:14)
at io.mockk.impl.recording.states.StubbingState.recordingDone(StubbingState.kt:8)
Any clue on what's I am doing wrong? Official docs suggest using such technique against private properties but for me it doesn't work and I'm using latest on this moment version of MockK which is v1.10.0.
Though for private methods it is working like a charm. I am able to test the private method in this logic.
This is a problem with some Kotlin optimisations. According to MockK author "Brief explanation. It is nearly impossible to mock private properties as they don't have getter methods attached. This is kind of Kotlin optimisation and solution is major change."
More info can be found on these 2 Github issues:
https://github.com/mockk/mockk/issues/263
https://github.com/mockk/mockk/issues/104

How to zip two Observables in Android?

In my app I have two services and both of them have a method that makes a requests and then returns an Observable of different type.
I want to display in a RecyclerView a list composed of the result of combining these two Observables. I googled about this and found the zip() method that seems to do exactly what I want. I'm trying to implement it but I don't know how to do it correctly.
While I was googling, I came up with this this article which seems to explain it clearly. Even though the author is using Singles while I am using Observables.
As far as I understand how zip() works, I know I have to pass every Observable I want to "zip" and then I must specify a function that will compose my final Observable, right?
This is my code so far:
interface InvitationService {
#GET("foo/{userId}")
fun getFooByUser(#Path("userId") userId: String): Observable<Response<ArrayList<Foo>>>
}
interface InvitationService {
#GET("bar/{userId}")
fun getBarByUser(#Path("userId") userId: String): Observable<Response<ArrayList<Bar>>>
}
class FooRemoteDataSource : FooDataSource {
private var apiService: FooService
fun getFooByUser(userId:String) {
return apiService.getFooByUser(userId)
}
}
class BarRemoteDataSource : BarDataSource {
private var apiService: BarService
fun getBarByUser(userId:String) {
return apiService.getBarByUser(userId)
}
}
class FooRepository(private val remoteDataSource: InvitationRemoteDataSource) : FooDataSource {
override fun getFooByUser(userId: String): Observable<Response<ArrayList<Foo>>> {
return remoteDataSource.getFooByUser(userId)
}
}
class BarRepository(private val remoteDataSource: BarRemoteDataSource) : BarDataSource {
override fun getBarByUser(userId: String): Observable<Response<ArrayList<Bar>>> {
return remoteDataSource.getBarByUser(userId)
}
}
And here is where I'm actually stuck:
class ListPresenter(var listFragment: ListContract.View?,
val fooRepository: FooRepository,
val barRepository: BarRepository) : ListContract.Presenter {
fun start() {
loadList()
}
private fun loadLists() {
//HERE IS WHERE IM STUCK
Observable.zip(fooRepository.getFooByUser(userId).subscribeOn(Schedulers.io()),
barRepository.getBarByUser(userId).subscribeOn(Schedulers.io()),
)
// AFTER 'ZIPPING' THE OBSERVABLES
// I NEED TO UPDATE THE VIEW ACCORDINGLY
}
}
I don't know how to call zip() properly, I know that I must pass a function but I don't get it because in the article linked above the author is using a Function3 because he has 3 Observables.
As I only have 2, I don't know how to do it. If open curly braces after a comma inside the method args, it requires me to return a BiFunction<ArrayList<Foo>, ArrayList<Bar>> which is what I don't know how to specify.
Would someone explain it to me?
For Kotlin you should use RxKotlin rather than RxJava. BiFunction, Function3 come from RxJava. With RxKotlin you can use lambdas instead.
As far as I understand how zip() works, I know I have to pass every Observable I want to "zip" and then I must specify a function that will compose my final Observable, right?
Correct, and here is a minimal example, which demonstrates how to do it.
Example 1
val observable1 = listOf(1, 2, 3).toObservable()
val observable2 = listOf(4, 5, 6).toObservable()
val zipped = Observables.zip(observable1, observable2) { o1, o2 -> o1 * o2}
In this example you have two observables, each emitting integers. You pass them to zip and as third argument a lambda which defines a way to "cobmine them". In this case it multiplies them.
The resulting observable zipped will emit: 4, 10 and 18.
Example 2
Here another example zipping three observables which are not all of the same type:
val obs1 = listOf("on", "tw", "thre").toObservable()
val obs2 = listOf("n", "o", "e").toObservable()
val obs3 = listOf(1, 2, 3).toObservable()
val zipped = Observables.zip(obs1, obs2, obs3) { o1, o2, o3 ->
"$o1$o2 = $o3"
}
Here, each element of the resulting observable will be a string: "one = 1", "two = 2", "three = 3"
Zipping two Observables of different types using BiFunction
override fun getCommoditiesAndAddresses() {
view.showProgress()
view.hideViews()
Observable.zip(Commo24Retrofit.createAuthService(RateAPIService::class.java)
.getCommodities(),
Commo24Retrofit.createAuthService(RateAPIService::class.java)
.getLocations(GetLocationsRequest(getOrgId())),
BiFunction { commodityResponse: GetCommoditiesResponse, locationsResponse: GetLocationsResponse ->
handleCommoditiesAndAddresses(commodityResponse, locationsResponse)
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
view.hideProgress()
view.showViews()
view.handleCommodities(it?.commodities)
view.handleLocations(it?.locations)
}, { throwable ->
view.hideProgress()
view.handleFailure(throwable.getErrorMessage(context))
})
}
Look, how I'm handling the response:
private fun handleCommoditiesAndAddresses(commodityResponse: GetCommoditiesResponse, locationsResponse: GetLocationsResponse): CommoditiesAddresses {
return CommoditiesAddresses(commodityResponse.commodityList, locationsResponse.addressList)
}
Here, check the API Service:
interface RateAPIService {
#POST("get-org-address")
fun getLocations(#Body getLocationsRequest: GetLocationsRequest): Observable<GetLocationsResponse>
#POST("get-commodity-list")
fun getCommodities(): Observable<GetCommoditiesResponse>
}
If you have any doubt you can comment it out.

Return value of if/else condition, but also run code

I'm a newbie to Kotlin, but I freaking love it. There's a wonderful feeling when you can turn three lines of code into one. Now I often find myself looking at my code and thinking "there's some redundancy here. There should be a more concise way to do it." And when I do some searching, often Kotlin does provide a more succinct way.
Here's a problem where I FEEL there should be a simple, concise Kotlin solution, but am not aware of it. Hopefully you can enlighten me!
Take code like this contrived example:
fun doSomething(): Boolean {
if (randomInt % 2 == 0) {
foo = Foo()
true
} else {
bar = null
false
}
}
Based on an if condition, I want to run some code and then return the value of the condition. It just bothers me that I have to explicitly say "if the condition is true, return true. If it is false, return false." It seems redundant. Of course, I could say return randomInt % 2, but I want to run code based on if it is true.
Is there a magic operator Kotlin has that I don't know about? Should I make a function to handle this situation for me, although the syntax of calling that would be different than an if statement? Is there some kind of mapping function, or some way to overload if? It seems like there should be a concise, clever answer, but it's not coming to me.
You can refactor your code a bit so that the return and code happen in different places:
fun doSomething(): Boolean {
val isEven = randomInt % 2 == 0
if (isEven) foo = Foo() else bar = null
return isEven
}
I upvoted some of the answers. I should have considered splitting out the condition to a variable or having the true/false test after the condition.
But after sleeping on it, I have another solution.
New utility functions:
fun Boolean.onTrue(block: () -> Unit): Boolean {
if (this) block()
return this
}
fun Boolean.onFalse(block: () -> Unit): Boolean {
if (!this) block()
return this
}
And then my original code sample can be condensed to:
fun doSomething() = (randomInt % 2).onTrue { foo() }.onFalse { bar = null }
This is the most concise option, although it has its own disadvantages. Nothing prevents the user from, say, calling onTrue() twice or calling onFalse() before onTrue(). It looks quite different from a standard if/else, and if you use both paths, both onTrue() and onFalse() have to check the condition. And then, of course, there's remembering to use the new utility function instead of standard Kotlin operators. Still, it has an appealing brevity. I'll be interested to see what other people think.
You may explore some useful extension functions from Kotlin's standard library. For example, you may use apply:
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
Here, randomInt % 2 == 0 will be the return value of apply.
fun doSomething(): Boolean = (randomInt % 2 == 0).apply { if (this) foo = Foo() else bar = null }
Updates: If you prefer a more readable code, it is not a good solution. Have fun :)
It's actually quite simple, you just have to get used to Kotlin's fantastic stdlib functions like apply, with and let. In this case, apply is what you need:
fun idiomatic(myInt: Int) = (myInt % 2 == 0).apply {
if (this) println("is equal") else println("in not equal")
}
What happens: apply is invoked on Any object, a Boolean (the condition) in this case, which directly becomes the functions receiver, referred to as this. The important thing is, that apply will, after the supplied code block has been executed, return its receiver, which is your Boolean here.
I hope this is what you need!
#pixel-elephant's solution looks concise and good.
However, from the clean code perspective, your doSomething() function is doing two things.
If possible you can separate it out to two functions and move the if check to the topmost function:
if ( randomInt.isEven() ) {
doSomethingEven()
// ...
} else {
doSomethingOdd()
// ...
}

How to pass null to an Observable with nullable type in RxJava 2 and Kotlin

I initialize my variable like this:-
val user: BehaviorSubject<User?> user = BehaviorSubject.create()
But I can't do this. IDE throws an error:-
user.onNext(null)
And doing this, IDE says u will never be null:-
user.filter( u -> u!=null)
As Guenhter explained, this is not possible. However, instead of proposing the null-object pattern, I'd recommend an implementation of the Optional type:
data class Optional<T>(val value: T?)
fun <T> T?.asOptional() = Optional(this)
This makes your intent much clearer, and you can use a destructuring declaration in your functions:
Observable.just(Optional("Test"))
.map { (text: String?) -> text?.substring(1)?.asOptional() }
.subscribe()
Using the null-object pattern here can cause more bugs than it solves.
If you use rxkotlin/rxjava 2.0 (I assume so) than the answer is: you can't. The reason is explained here.
This is a break of the interface. Have a look at the Observable Interface
public interface Observer<T> {
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
/** ... */
void onSubscribe(#NonNull Disposable d);
/** ... */
void onNext(#NonNull T t);
/** ... */
void onError(#NonNull Throwable e);
...
The #NonNull will be considered by the Kotlin compiler and therefore you CAN'T pass null.
Even if you could, the onNext would immediately throw an error:
#Override
public void onNext(T t) {
if (t == null) {
onError(new NullPointerException("onNext called with null. Null values are generally not allowed in 2.x operators and sources."));
return;
}
...
}
If you really need such a thing as null you have to fake it. e.g. by creating a static object of User which represents your null-element.
e.g.
data class User(val username, val password) {
companion object {
val NULL_USER = User("", "")
}
}
...
val user = BehaviorSubject.create<User>()
...
user.onNext(User.NULL_USER)
...
user.filter { it !== User.NULL_USER }
But if is somehow possible, try to avoid the null concept and maybe think of another solution where this isn't needed.
Thank you very much for all your answers but I ultimately went with this solution:-
class UserEnvelope(val user:User?) {}
And using this in the observables.
This best suited my requirements.
I am new to Kotlin so I don't know how to use Optionals. But from what I understand, I would have to typecast it to User type everytime I need to observe the values right?
To implement the solution mentioned in the nhaarman's answer, you can use the util class Optional (doc) from the Android SDK which was added in API level 24.
If your app's minSdkVersion less than 24 then you still need to implement it by yourself.
Since RxJava 2 does not support null values, there are some other acceptable solutions you can use:
Work with a custom or third party wrapper library of Optionals like some of the posted answers suggest. When I got rid of Java in favour of Kotlin, Optionals went away in the same package since Kotlin per se supports nullability as part of its type System. Just by this change the code was much more clearer, and I personally don't want to get Optionals back in my code as long as I can avoid them.
Emit Any class instances with your subject type. For example you could create an Empty.INSTANCE enum class which would emulate the null value and then filter by the enum class.
The last one is the one I use and prefer being a variant of the previous solution and is based on specialisations. Our friends of JetBrains always emphasise that classes are very cheap in Kotlin, so this would be a quick example to distinguish logged users and not logged ones:
abstract class SessionUser
sealed class LoggedUser(val username: String, val password: String) : SessionUser()
sealed class LogoutUser : SessionUser()
private val user = BehaviorSubject.create<SessionUser>()
private val loggedUser =
user.filter { it is LoggedUser }.cast(LoggedUser::class.java)
fun login(username: String, password: String) {
user.onNext(LoggedUser(username, password))
}
fun logout() {
user.onNext(LogoutUser())
}
I've taken an approach similar to Optional<User> and UserEnvelope. I make a simple User class and a ReifiedUser class that inherits from it. The User class has a companion object that has a NONE instance. The BehaviorSubject is instantiated with the User.NONE instance. It looks something like this:
open class User {
companion object {
val NONE = User()
}
}
class ReifiedUser(
#field:JsonProperty(J.FirstName) val firstName: String,
#field:JsonProperty(J.LastName) val lastName: String
) : User()
My BehaviorSubject is instantiated like this:
val activeUser: BehaviorSubject<User> = BehaviorSubject.createDefault(User.NONE)
And wherever I need to use activeUser I either flatMap it to Observable.empty() if it's NONE or just figure out what it is and what to do in the subscriber.
I don't like mixing java Optional with kotlin nullable because mixing map and let gets really confusing and ugly. This way it's very obvious what's going on.
I think it makes more sense to write a container class such as Result. An example of that would be
data class Result<T>(value: T?, error: Throwable?)
Usage
Observable.create { observer ->
upstreamService.listen(object: UpstreamListener {
onSuccess(data: User) {
observer.onSuccess(Result(data))
}
onError(exception: Throwable) {
observer.onSuccess(Result(null, exception))
}
}
}

Unit test on Kotlin Extension Function on Android SDK Classes

Kotlin extension function is great. But how could I perform unit test on them? Especially those that is of Android SDK provided class (e.g. Context, Dialog).
I provide two examples below, and if anyone could share how I could unit test them, or if I need to write them differently if I really want to unit test them.
fun Context.getColorById(colorId: Int): Int {
if (Build.VERSION.SDK_INT >= 23)
return ContextCompat.getColor(this, colorId)
else return resources.getColor(colorId)
}
and
fun Dialog.setupErrorDialog(body : String, onOkFunc: () -> Unit = {}): Dialog {
window.requestFeature(Window.FEATURE_NO_TITLE)
this.setContentView(R.layout.dialog_error_layout)
(findViewById(R.id.txt_body) as TextView).text = body
(findViewById(R.id.txt_header) as TextView).text = context.getString(R.string.dialog_title_error)
(findViewById(R.id.txt_okay)).setOnClickListener{
onOkFunc()
dismiss()
}
return this
}
Any suggestion would help. Thanks!
The way I'm testing extension functions on Android classes at the moment is by mocking the Android class. I know, this is not an optimal solution as it mocks the class under test and requires certain knowledge about how the function works (as it is always the case when mocking), but as extension functions are internally implemented as static functions I guess it's acceptable until someone comes up with something better.
As an example consider the JsonArray class. We've defined an extension function for receiving the last item's index:
fun JSONArray.lastIndex() = length() - 1
The according test (using the Spek test framework and mockito-kotlin) looks like this.
#RunWith(JUnitPlatform::class)
object JsonExtensionTestSpec : Spek({
given("a JSON array with three entries") {
val jsonArray = mock<JSONArray> {
on { length() } doReturn 3
}
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be 2") {
lastIndex shouldBe 2
}
}
}
given("a JSON array with no entries") {
val jsonArray = mock<JSONArray>({
on { length() } doReturn 0
})
on("getting the index of the last item") {
val lastIndex = jsonArray.lastIndex()
it("should be -1") {
lastIndex shouldBe -1
}
}
}
})
The difficulty with your functions is, that they also use Android classes internally. Unfortunately I don't have a solution for this right now.

Categories

Resources