I am using mockk Kotlin library. I have a service Service that has a private method calling a 3d party client
class Service {
fun foo() {
bar()
}
private fun bar() {
client = Client()
client.doStuff()
}
}
Now in my test I need to mock Client, e.g.
#Test
fun `my func does what I expect` {
}
I need to also mock what doStuff returns. How do I achieve this in Kotlin mockk?
Firstly, you should never instantiate a dependency like Client inside your service class since you cannot access it to provide a Mock. Let's deal with that first...
class Client { // this is the real client
fun doStuff(): Int {
// access external system/file/etc
return 777
}
}
class Service(private val client: Client) {
fun foo() {
bar()
}
private fun bar() {
println(client.doStuff())
}
}
and then this how to use Mockk
class ServiceTest {
private val client: Client = mockk()
#Test
fun `my func does what I expect`() {
every { client.doStuff() } returns 666
val service = Service(client)
service.foo()
}
}
Related
I have this Java code:
Services.nfGetIncreasedRiskZones(new Callback() {
#Override
public void onResponse(Call call, Response response) {
}
#Override
public void onFailure(Call call, Throwable t) {
}
});
The service it is calling:
public static void nfGetIncreasedRiskZones(Callback callback) {
NfGetIncreasedRiskZones service = App.getRetrofitWithHeaders(App.getBaseUrl()).create(NfGetIncreasedRiskZones.class);
Call call = service.getRiskZones();
call.enqueue(callback);
}
And the NfGetIncreasedRiskZones interface:
interface NfGetIncreasedRiskZones {
#GET(Constants.NfGetIncreasedRiskZones)
Call getRiskZones();
}
The retrofit Callback interface look like this:
public interface Callback<T> {
void onResponse(Call<T> call, Response<T> response);
void onFailure(Call<T> call, Throwable t);
}
How can I convert the first part, the "Services.nfGetIncreasedRiskZones", to Kotlin
Thank you
My best guess here is, that you are interested in how to instantiate anonymous interfaces in Kotlin and this is probably how you'd do it. Here is a quick example I hacked together on my Kotlin REPL. It's probably not the same interface you are using but it should work the same way.
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Response
import java.io.IOException
val callback = object: Callback {
override fun onResponse(call: Call, t: Response?){
println("onResponse")
}
override fun onFailure(call: Call?, t: IOException?){
println("onFailure")
}
}
callback.onFailure(null, null) //prints "onFailure"
callback.onResponse(null, null) //prints "onSuccess"
So basically you need the object: in front of your interface and you don't need the () after the interface. The rest is more or less the same except for the slightly different syntax.
BTW: If you are sure that the parameters of the methods are never null, you don't need the ? after the parameter type.
Here I implemented a sample Service and set the anonymous callback, then run it:
class Service {
companion object {
var cb: Callback? = null
fun set(callback: Callback){
cb = callback
}
fun run() {
cb?.onResponse(null, null)
}
}
}
Service.set(object: Callback{
override fun onResponse(call: Call?, t: Response?){
println("onResponse")
}
override fun onFailure(call: Call?, t: IOException?){
println("onFailure")
}
})
Service.run() //prints "onResponse"
I am testing an API written in Kotlin using the KTOR framework. For the testing, I am using JUnit5 and Mockito. There is a route class where a route is defined which I need to test. Here is the route class :-
fun Application.configureRouting() {
routing {
post("/someRoute") {
val service = MyService()
val request: JsonNode = call.receive()
launch {
service.dummyFunction(request)
}
val mapper = ObjectMapper()
val responseStr = "{\"status\":\"success\",\"message\":\"Request has been received successfully\"}"
val response: JsonNode = mapper.readTree(responseStr)
call.fireHttpResponse(HttpStatusCode.OK, response)
}
}
}
This is the test case I am writing for it :-
class RouteTest {
#Mock
var service = MyService()
// read the configuration properties
private val testEnv = createTestEnvironment {
config = HoconApplicationConfig(ConfigFactory.load("application.conf"))
}
#Before
fun setUp() = withApplication(testEnv) {
MockitoAnnotations.openMocks(MyService::class)
}
#Test
fun test() = withApplication(testEnv) {
withTestApplication(Application::configureRouting) {
runBlocking {
Mockito.`when`(service.dummyFunction(Mockito.any()).thenReturn(true)
with(handleRequest(HttpMethod.Post, "/someRoute") {
setBody("some body")
}) {
assertEquals(HttpStatusCode.OK, response.status())
}
}
}
}
}
When I run the test, it calls the actual "dummyFunction()" method instead of the mocked one and hence, it is failing. Am I doing something wrong?
Because your service in test is different from the service you mocked. To solve this, you need to inject the service into your class, or pass the service as an argument.
Read more: IoC, DI.
The simplest way to solve your problem is to define the service parameter for the configureRouting method and pass a corresponding argument in the test and production code when calling it.
fun Application.configureRouting(service: MyService) {
routing {
post("/someRoute") {
val request: JsonNode = call.receive()
launch {
service.dummyFunction(request)
}
val mapper = ObjectMapper()
val responseStr = "{\"status\":\"success\",\"message\":\"Request has been received successfully\"}"
val response: JsonNode = mapper.readTree(responseStr)
call.fireHttpResponse(HttpStatusCode.OK, response)
}
}
}
class RouteTest {
#Mock
var service = MyService()
private val testEnv = createTestEnvironment {
config = HoconApplicationConfig(ConfigFactory.load("application.conf"))
}
#Test
fun test() = withApplication(testEnv) {
withTestApplication({ configureRouting(service) }) {
runBlocking {
// Your test...
}
}
I am trying to writer an HTTP server using a Vert.x CoroutineVerticle, but I get an error lateinit property vertxInstance has not been initialized. It seems like the init(...) function of CoroutineVerticle is not being called when the verticle is deployed.
package http
import io.vertx.core.DeploymentOptions
import io.vertx.core.Vertx
import io.vertx.ext.web.Router
import io.vertx.kotlin.core.http.listenAwait
import io.vertx.kotlin.coroutines.CoroutineVerticle
class CoTest : CoroutineVerticle() {
private var router = createRouter()
override suspend fun start() {
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8182))
}
private fun createRouter() = Router.router(vertx).apply {
get("/favicon.ico").handler { req ->
req.response().end("NO")
}
}
}
fun main() {
class V : ServerVertxCo(8182) {}
Vertx.vertx().deployVerticle(CoTest::class.java.canonicalName)
}
The problem is the order you do things in your class.
The order for vertx is:
Create the vertx instance
Deploy a verticle
Verticle class constructor is called (vertx is not "injected" to it yet)
Vertx does some internal initializations
Vertx calls the verticle's start function
You use private var router = createRouter() in the class (outside the start block) and at that point vertx is not "injected" yet to the class so you get an exception.
If you move it to the start block it should work as you expect:
class CoTest : CoroutineVerticle() {
override suspend fun start() {
val router = createRouter()
vertx.createHttpServer()
.requestHandler(router)
.listenAwait(config.getInteger("http.port", 8182))
}
private fun createRouter() = Router.router(vertx).apply {
get("/favicon.ico").handler { req ->
req.response().end("NO")
}
}
}
suspend fun main() {
val vertx = Vertx.vertx()
vertx.deployVerticleAwait(CoTest::class.java.canonicalName)
}
I'm looking for an elegant solution to the following.
I'd like to implement a Wrapper class that:
Accepts 2 implementations of the same Interface, and returns a new instance of that same Interface.
Any method call to the Wrapper object, tries to call the same method on the 1st implementation.
If the first call results into UnsupportedOperationException, then the 2th implementation should be used instead.
interface API {
fun getData(): String
}
class Main: API {
override fun getData(): String {
throw UnsupportedOperationException()
}
}
class Fallback: API {
override fun getData(): String {
return "data"
}
}
class Wrapper {
companion object {
fun getInstance(main: API, fallback: API): API {
// TODO
}
}
}
class Test {
#Test
fun `invokes the fallback instance`() {
val wrapper = Wrapper.getInstance(Main(), Fallback())
val response = wrapper.getData()
assertEquals(response, "data")
}
}
The best thing I have come up with so far is Delegate with Overrides:
class Wrapper(fallback: API): API by Main() {
val fallback = fallback
override fun getData(): String {
return fallback.getData()
}
}
What I don't like about this solution is that:
It requires overriding each unsupported operation
It gets quite verbose as the Interface grows into a complex multilevel structure with more sub interfaces
I'd also like to avoid Reflection for performance reasons and because this is a Kotlin Multiplatform project.
Any suggestions are appreciated.
Thanks,
Juan
Your proposed solution won't work because it will always favor the fallback for any overridden function.
There's no solution for your needs that can avoid having to manually handle every function of your interface. But you can have an intermediate function that handles the cascading selection of implementation for functions with the same signature.
class Wrapper (private val delegates: Array<out API>): API {
companion object {
fun getInstance(vararg delegates: API) = Wrapper(delegates)
}
private fun <R> delegate0Arg(function: API.() -> R): R {
for (delegate in delegates) {
try {
return delegate.function()
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override val name: String get() = delegate0Arg(API::name)
override fun getData(): String = delegate0Arg(API::getData)
}
But you would need additional functions to handle each unique number of arguments the interface functions have.
private fun <T, R> delegate1Arg(t: T, function: API.(t: T) -> R): R {
for (delegate in delegates) {
try {
return delegate.function(t)
} catch (e: UnsupportedOperationException) {
// continue
}
}
throw UnsupportedOperationException()
}
override fun getData(x: String) = delegate1Arg(x, API::getData)
I am new to Guice. I am trying to use requestInjection to inject the dependencies of a kotlin singleton object in this way.
APPROACH 1:
class SampleTest {
#Test
fun test() {
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestInjection(A)
}
})
assertEquals("Hello world", A.saySomething())
}
}
object A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
But I am getting this error:
kotlin.UninitializedPropertyAccessException: lateinit property b has not been initialized
If I change A to a class with no-arg constructor, it works.
APPROACH 2:
class SampleTest {
#Test
fun test() {
val a = A()
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestInjection(a)
}
})
assertEquals("Hello world", a.saySomething())
}
}
class A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
Instead, if I change requestInjection to requestStaticInjection, it also works.
APPROACH 3:
class SampleTest {
#Test
fun test() {
Guice.createInjector(object: KotlinModule() {
override fun configure() {
requestStaticInjection<A>()
}
})
assertEquals("Hello world", A.saySomething())
}
}
object A {
#Inject
private lateinit var b: B
fun saySomething(): String {
return b.sayHello()
}
}
class B {
fun sayHello(): String {
return "Hello world"
}
}
Why didn't APPROACH 1 work? Why did APPROACH 2 and APPROACH 3 work?
Kotlin's objects are treated as language static singletons, i.e. their initialization/instantiations happens outside the scope of the dependency injection framework.
Therefor, when using the KotlinModule to inject an object, you have to use requestStaticInjection like in APPROACH 3, or change that object to a class, so that the Guice KotlinModule sees it as non-static, as presented in APPROACH 2
Hope that clarifies things a bit.