https://kotlinlang.org logo
#android
Title
# android
p

Pascal How

02/11/2018, 11:53 PM
Ah ok. So far I have been testing extension functions using assert to verify expected and actual results. However, since on the surface, extensions look like methods of the class being extended, there may be scenarios where you would want a way of mocking that function if that class is being injected as a dependency
g

gildor

02/12/2018, 1:37 AM
Do you have example of such scenario? Usually when you want to mock extension function you actually don’t need extension function, but a class or member function
p

Pascal How

02/12/2018, 10:29 AM
So let’s say we want to extend a
Car
class with an extension function
hadAccident()
because the original class did not have this method and it is from a third party library so we cannot add member functions Then if another class
ServiceCompany
takes a
car
and a
mechanic
as dependency and one of its methods does
Copy code
fun organiseRepair() {
    if(car.hadAccident) {
        mechanic.doFullRepair()
    } else {
        mechanic.doMinimalCheck()
    }
}
When testing with Mockito you would then want something like
Copy code
fun givenCarHadAccident_whenOrganiseRepair_thenDoFullRepair() {
    `when`(car.hadAccident).thenReturn(true)

    serviceCompany.organiseRepair()

    verify(mechanic).doFullRepair()
}
g

gildor

02/12/2018, 10:31 AM
Where did you get
mechanic
?
actually it doesn’t matter in this case
p

Pascal How

02/12/2018, 10:32 AM
So both mechanic and car are dependencies to the class ServiceCompany. Then we want to test the ServiceCompany class
g

gildor

02/12/2018, 10:33 AM
Just create a Car object for case when hadAccident returns true or returns false
I don’t see why you should mock extension function in this case if you can construct required Car object
p

Pascal How

02/12/2018, 10:34 AM
Copy code
class ServiceCompany(val car:Car, val mechanic: Mechanic) {
fun organiseRepair() {
    if(car.hadAccident) {
        mechanic.doFullRepair()
    } else {
        mechanic.doMinimalCheck()
    }
}
}
Yes I can construct car but what if
Car
is from a third party library and it does not have the
hadAccident
method?
g

gildor

02/12/2018, 10:35 AM
I don’t see any problem
extension function uses only public methods
so you can mock Car or create car with required data (second even more clear approach)
for example if hadAccident is something like:
Copy code
fun Car.hadAccident() = this.accidents.size > 0
You can just do:
Copy code
fun givenCarHadAccident_whenOrganiseRepair_thenDoFullRepair() {
    val carWithAccident = Car(accidents = listOf(Accident()))
    val serviceCompany = ServiceCompany(carWithAccident, mechanic)

    serviceCompany.organiseRepair()

    verify(mechanic).doFullRepair()
}
you don’t need any mocking there
this is why I asked about case
and even if it’s impossible to create Car with required data (no interfaces, no required constructors), you can just mock Car instance but do not mock extension function
And just in case Great Mockk library http://mockk.io/ from @oleksiyp can mock extension functions, but my opinion that in 99% cases you should avoid it, because you can and should use another test approaches. In general mocking static members consider by many as bad practice. One of the reasons, because developers starts to use it for cases when it shouldn’t be used, because you can easily test without mocking at all, like in your example
p

Pascal How

02/12/2018, 10:50 AM
Yea I was wondering more about cases where you cannot create Car with accidents as parameter and all you can do is
val car = Car()
(might be a bad example) Then one way around that is to provide an interface and the class that implements that can provide a method wrapping the call to the extension function. During test, you would then test the mock instance instead Thanks! I saw these libraries but I did not think it was a good idea to use them
g

gildor

02/12/2018, 10:53 AM
If you cannot create an instance of class, just use mocks, you probably need mock of
car
even if you mock
hadAccident
. So I really don’t see any reason to use mocked extension function in this case
also, you actually test implementation details of
serviceCompany
instead of testing
serviceCompany
results, so it’s also bad practice imho
even if
hadAccident
would be member function I still suggest to use real instance of Car with proper data for cases when it’s possible
p

Pascal How

02/12/2018, 11:10 AM
Cool! Makes more sense now. Thanks for your time 👍