Skip to content

Instantly share code, notes, and snippets.

@aoriani
Created December 25, 2023 03:13
Show Gist options
  • Save aoriani/17ddeca36eda74d7eb4caf3abd8ce56d to your computer and use it in GitHub Desktop.
Save aoriani/17ddeca36eda74d7eb4caf3abd8ce56d to your computer and use it in GitHub Desktop.
A simple implementation of a mock library using ByteBuddy
package org.example
import net.bytebuddy.ByteBuddy
import net.bytebuddy.dynamic.DynamicType
import net.bytebuddy.implementation.FixedValue
import net.bytebuddy.matcher.ElementMatchers
import org.junit.jupiter.api.Test
import java.net.Socket
import kotlin.reflect.KFunction
import kotlin.reflect.jvm.javaMethod
import kotlin.test.assertEquals
import kotlin.test.assertTrue
interface MockScopeDsl {
fun <T> whenever(method: KFunction<T>): KFunction<T>
infix fun <T> KFunction<T>.returns(returnValue: T)
}
class MockScopeDslImpl : MockScopeDsl {
val mockedMethods = mutableMapOf<KFunction<*>, Any?>()
override fun <T> whenever(method: KFunction<T>) = method
override fun <T> KFunction<T>.returns(returnValue: T) {
mockedMethods[this@returns] = returnValue
}
}
inline fun <reified T : Any> mock(configBlock: MockScopeDsl.() -> Unit): T {
val mockScope = MockScopeDslImpl()
mockScope.configBlock()
var subClassProto: DynamicType.Builder<T> = ByteBuddy().subclass(T::class.java)
mockScope.mockedMethods.forEach { (kFunction, returnValue) ->
subClassProto = subClassProto
.method(ElementMatchers.`is`(kFunction.javaMethod!!))
.intercept(FixedValue.value(returnValue!!))
}
val clazz = subClassProto.make().load(T::class.java.classLoader).loaded as Class<T>
return clazz.getDeclaredConstructor().newInstance()
}
class SocketTest {
private val mockedSocket: Socket = mock {
whenever(Socket::getLocalPort) returns 42
whenever(Socket::getInputStream) returns "foo bar".byteInputStream()
whenever(Socket::isBound) returns true
}
@Test
fun test() {
assertEquals(42, mockedSocket.localPort)
assertTrue(mockedSocket.isBound)
assertEquals("foo bar", mockedSocket.getInputStream()
.bufferedReader()
.use { it.readText() }
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment