In addition to using the Mockk library, which is quite convenient, you could mock an object simply with Mockito and reflection. A Kotlin object is just an ordinary Java class with a private constructor and a static INSTANCE field, with reflection you can replace the INSTANCE value INSTANCE a mocked object. After the test, the original must be restored so that the change does not affect other tests.
Using Mockito Kotlin (add extension configuration as described here to make fun of the final classes):
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
The first pleasure is to replace the value of the INSTANCE static field in the object class and return the previous value
fun <T> replaceObjectInstance(clazz: Class<T>, newInstance: T): T { if (!clazz.declaredFields.any { it.name == "INSTANCE" && it.type == clazz && Modifier.isStatic(it.modifiers) }) { throw InstantiationException("clazz ${clazz.canonicalName} does not have a static " + "INSTANCE field, is it really a Kotlin \"object\"?") } val instanceField = clazz.getDeclaredField("INSTANCE") val modifiersField = Field::class.java.getDeclaredField("modifiers") modifiersField.isAccessible = true modifiersField.setInt(instanceField, instanceField.modifiers and Modifier.FINAL.inv()) instanceField.isAccessible = true val originalInstance = instanceField.get(null) as T instanceField.set(null, newInstance) return originalInstance }
Then you could have some fun creating a mock instance of object and replace the original value with the mocked one, returning the original so it could be reset later
fun <T> mockObject(clazz: Class<T>): T { val constructor = clazz.declaredConstructors.find { it.parameterCount == 0 } ?: throw InstantiationException("class ${clazz.canonicalName} has no empty constructor, " + "is it really a Kotlin \"object\"?") constructor.isAccessible = true val mockedInstance = spy(constructor.newInstance() as T) return replaceObjectInstance(clazz, mockedInstance) }
Add some Kotlin sugar
class MockedScope<T : Any>(private val clazz: Class<T>) { fun test(block: () -> Unit) { val originalInstance = mockObject(clazz) block.invoke() replaceObjectInstance(clazz, originalInstance) } } fun <T : Any> withMockObject(clazz: Class<T>) = MockedScope(clazz)
And finally, given the object
object Foo { fun bar(arg: String) = 0 }
You can test it this way.
withMockObject(Foo.javaClass).test { doAnswer { 1 }.whenever(Foo).bar(any()) Assert.assertEquals(1, Foo.bar("")) } Assert.assertEquals(0, Foo.bar(""))