Skip to content

Instantly share code, notes, and snippets.

@daverix
Created April 6, 2019 15:05
Show Gist options
  • Save daverix/866a116bbc4e71abeadfe30b1b04f5a9 to your computer and use it in GitHub Desktop.
Save daverix/866a116bbc4e71abeadfe30b1b04f5a9 to your computer and use it in GitHub Desktop.
kotlin home made mock
package net.daverix.mock
import com.google.common.truth.Truth.assertThat
import net.daverix.transparentcalendarwidget.db.EventsProvider
import net.daverix.transparentcalendarwidget.model.CalendarEvent
import org.junit.Test
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
class Test {
@Test
fun test() {
val myMock = mock<EventsProvider>()
val widget = createTestWidget(2)
myMock.calling("getEvents") {
parameter(0) { it == widget }
returns(emptyList<CalendarEvent>())
}
val events = myMock.instance.getEvents(widget, emptyMap())
assertThat(events).isNotNull()
myMock.verify("getEvents") {
parameter(0) { it == widget }
parameter(1) { it is Map<*, *> && it.isEmpty() }
returns { it is List<*> && it.isEmpty() }
}
}
}
interface Mock<T> {
val instance: T
fun verify(method: String, call: Call.() -> Unit)
fun calling(method: String, call: Call.() -> Unit)
}
interface Call {
fun parameter(index: Int, func: (Any?) -> Boolean)
fun returns(value: Any?)
fun returns(func: (Array<Any?>) -> Any?)
}
class Invocation(val name: String,
val args: Array<Any?>)
class UserCall : Call {
val parameterFuncs = mutableListOf<Pair<Int, (Any?) -> Boolean>>()
var returnFunc: ((Array<Any?>) -> Any?)? = null
override fun parameter(index: Int, func: (Any?) -> Boolean) {
parameterFuncs += index to func
}
override fun returns(value: Any?) {
returnFunc = { value }
}
override fun returns(func: (Array<Any?>) -> Any?) {
returnFunc = func
}
}
class UserFunction(val name: String,
val func: Call.() -> Unit)
inline fun <reified T> mock(): Mock<T> {
val calls = mutableListOf<Invocation>()
val userFunctions = mutableListOf<UserFunction>()
val invocationHandler = InvocationHandler { _: Any, method: Method, args: Array<Any?> ->
calls += Invocation(method.name, args)
val userCall = userFunctions
.filter { it.name == method.name }
.map {
val userCall = UserCall()
it.func(userCall)
userCall
}
.sortedByDescending { it.parameterFuncs.size }
.firstOrNull {
it.parameterFuncs.isEmpty() ||
args.withIndex().all { (argIndex, value) ->
val func = it.parameterFuncs.filter { (paramIndex, _) -> paramIndex == argIndex }
.map { (_, func) -> func }
.firstOrNull()
func == null || func(value)
}
}
userCall?.returnFunc?.invoke(args)
}
val proxy = Proxy.newProxyInstance(T::class.java.classLoader,
arrayOf(T::class.java),
invocationHandler) as T
return object : Mock<T> {
override val instance: T = proxy
override fun verify(method: String, call: Call.() -> Unit) {
if (calls.isEmpty())
throw AssertionError("No calls have been made on this mock")
if (method !in calls.map { it.name })
throw AssertionError("method never called, however there was ${calls.size} other method calls")
val methods = calls.filter { it.name == method }
.filter {
val userCall = UserCall()
call(userCall)
userCall.parameterFuncs.isEmpty() ||
it.args.withIndex().any { (argIndex, value) ->
val func = userCall.parameterFuncs.filter { (paramIndex, _) -> paramIndex == argIndex }
.map { (_, func) -> func }
.firstOrNull()
func != null && !func(value)
}
}
if(methods.isNotEmpty())
throw AssertionError("${methods.size} method call(s) did not match:\n${methods.joinToString("\n") {
"${it.name}(${it.args.joinToString(", ")})"
}}")
}
override fun calling(method: String, call: Call.() -> Unit) {
userFunctions += UserFunction(method, call)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment