Hi, do you know a good alternative to the `verify`...
# test
k
Hi, do you know a good alternative to the
verify
method for KMP without using mocking libraries?
If that's not what you want, you could just make a Fake class implementing the interface, and use vars in that class to track function calls.
k
I know it is possible with mocking libs but I want to drop mocking tools. Flag switching is my current approach but it is... bad
d
Flag switching?
k
fake class with boolean
d
It does have it's advantages over mocking though... since if you change the interface, you'll get a compile error and you have ide refactorings
But you could always have some kind of traker class in your fake constructor that handles recording calls...
k
fakes are fine but creating special implementation just to track method invocation not so much
d
There's always reflection, but then you're back to mocks
k
not an option for kmp
d
True, that's why I posted that article on MockMP
It's supposed to generate the mocks with kmp
Which is a bit like using those tracker objects
The downside is the wiring with the every { ... } blocks that if you write your own fakes you could avoid.
j
verify
is an antipattern, tests should check the output based on an input, not checking if a method has or hasn't been called
d
I was about to say that, but there are some valid cases where verify might be needed... you manage to avoid it in all cases @Javier?
j
Yes, there is always an input and an output. If not it is not doing anything.
d
So you never had to check if some process was enacted as a result of some state?
You could just check the state, but you would have passing tests even if the process wasn't enacted as a result of that state...
Of course, you could consider the tracker val in the Fake as the "output"...
j
Some sample? If a function does a side effect changing a state, I only need to check that state.
d
Copy code
fun updateRss(...) {
  // Check if updating is needed
  if (...) {
    rssUpdater.update(...)
  }
}
j
Going more than that can lead to a mad world. If you don’t check that the method is executed, maybe is a different method what it is doing that to work, but what if your method is called and it is not doing the job but another it is doing it? You should check the rest of methods are not called You cannot put a line about when to stop.
that updateRss will mutate a state, it is nothing different about testing a viewmodel that contains a state and the vm has tons of functions that do not return anything but modifies the state.
d
You can, since usually that rssUpdater is an interface implementing a contract to the class calling it. So you test with an RssUpdaterFake for when it's called, and then you test the updater separately, then you have a few little smoke tests for the wiring.
I agree that some classes can be tested together for a certain behaviour, but the side-effects can be isolated (like db/network repos) and Faked, and they're tested by themselves as integration...
He has a nice presentation about this:

https://www.youtube.com/watch?v=mzznsq4jCHY

for c# (not too much code there, so it's really just theory)...
vm is different, since the "input" are the functions being called, and you can test the "output" which is the resulting state and side-effects being sent from, say, a Channel...
The wiring to the view shouldn't have any complex logic, and it's just wiring code, so there, I'd agree there wouldn't really be a need for testing so much...
j
There are multiple points about this. The short answer is: I wouldn't say I like unit tests, I prefer integrations test faking the deepest dependency, or none if possible. I would avoid unit tests outside libraries, utilities, or some hard-to-reproduce edge cases on integration tests. That updater is generating an output somewhere, and I would check that output, such as an in-memory modification, a new DB entry, a new file, or whatever. A simple sample of this can be Gradle. I have a custom Gradle plugin to publish Maven dependencies to Maven Central. Obviously, I am not going to test that publication within a remote server, but I publish it to a custom local maven repository, and I check the correct files are there. I am not going to check if the correct methods are executed N number of times.
k
It is not always possible to verify the output. If you are using for example library functions you cannot just fake the result and your only option is to verify if specific function is invoked.
j
Can you share a sample?
> library functions I guess you are referring to third-party libraries. I wouldn't test that at all. There are two options • The library has its tests • The library does not test anything so I would avoid it. And even with that, I need a real sample about why you need to use
verify
on those methods. The sample about publishing to Maven is exactly that. It has a lot of third party methods to configure the publication. I don't care at all about when and how they are called. I just care about the output.
k
my current case is a good example: https://github.com/kamgurgul/cpu-info/blob/master/shared/src/androidMain/kotlin/com/kgurgul/cpuinfo/components/RamCleanupComponent.android.kt Of course you don't want to test if
System.gc()
is called but you want to verify if
cleanup()
is called in some conditions here https://github.com/kamgurgul/cpu-info/blob/master/shared/src/commonMain/kotlin/com/kgurgul/cpuinfo/domain/action/RamCleanupAction.kt#L9 There is no visible outcome which you can check. Of course this is a simple case but the idea would be the same if for example method
doWork
would have more complex logic which should be tested.
d
In that case, I'd make RamCleanupComponent into an Interface, and use a fake
🔝 1
k
Then we will do the same logic as we discussed initially- fake with custom logic to mark invocation
d
I did something like this for Faking my use cases:
Copy code
class SLambdaFake2<A, B, R>(var result: R) : suspend (A, B) -> R {
    val calledWith = mutableListOf<Pair<A, B>>()
    val results = mutableListOf<R>()

    override suspend fun invoke(a: A, b: B): R {
        calledWith.add(Pair(a, b))
        results.add(result)

        return result
    }

    fun reset() {
        calledWith.clear()
    }
}

class SLambdaFake3<A, B, C, R>(var result: R) : suspend (A, B, C) -> R {
    val calledWith = mutableListOf<Triple<A, B, C>>()
    val results = mutableListOf<R>()

    override suspend fun invoke(a: A, b: B, c: C): R {
        calledWith.add(Triple(a, b, c))
        results.add(result)

        return result
    }
}
(my use cases are all fun interfaces with variable params)
You could always have such a thing for functions in an interface, use a
by
to create them in the Fake class and use a lambda to surround the function like:
Copy code
class SomeFake() {
  val fooTracker by tracker<String, Int, Foo>()

  fun foo(f: String, b: Int): Foo = fooTracker(f, b) {
    ... return Foo
  }
}

// In test
assert(someFake.fooTracker.result, Foo(...))
not the same implementation as I posted above, but it could be an easy-to-use tracker that could help in making fakes...
The advantage to Mock frameworks, is you can use custom logic in your Fake to generate reasonable results according to inputs...
Not in the case you stated, though... since what really matters is whether you called the function or not, then the tracker could just be a simple history of calls, the real benefit of such a framework that I mentioned is for testing call parameters... and times they got called without all the setup
every {. ... }
blocks of a mocking framework... It could be the code is a bit wrong, it's just an idea/general direction, but it could certainly be improved.
j
Probably the overhead of mocks takes more time in execution that checking the amount of memory before and after (I haven’t done that before, but it shouldn’t be two lines?)