I want to test some code that uses 3rd party code that calls kotlin.system.exitProcess(), defined as follows in the standard lib:
#kotlin.internal.InlineOnly
public inline fun exitProcess(status: Int): Nothing {
System.exit(status)
throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")
}
When exitProcess() is called, the JVM stops and further testing is impossible. I didn't manage to mock calls to exitProcess() with mockk. Is it possible?
Some further information:
The 3rd party lib is Clikt (https://ajalt.github.io/clikt/), a nice library for building a command line interface. A Clikt application parses the command line, and exits if this fails. This may be one of the rare reasons, where calling System.exit is OK. Surely there are more testable solutions, but anyway, when working with 3rd party libs, arguing what could be better done in the lib is obsolete.
What I actually want to test is, that my application writes the expected usage message when called with --help or wrong arguments.
I also tried to mock the call to System.exit() this way:
mockkStatic("java.lang.System")
every { System.exit(any()) }.throws(RuntimeException("blubb"))
which leads to another problem, als all calls to System are mocked then:
io.mockk.MockKException: every/verify {} block were run several times. Recorded calls count differ between runs
Round 1: class java.lang.System.getProperty(kotlin.ignore.old.metadata), class java.lang.System.exit(-630127373)
Round 2: class java.lang.System.exit(158522875)
Funnily enough, I managed to test this in Java with jmockit, like this:
public class MainTestJ {
private ByteArrayOutputStream consoleOut;
#BeforeEach
public void mockConsole() {
consoleOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(consoleOut));
}
#Test
public void can_mock_exit() {
new MockUp<System>() {
#Mock
public void exit(int exitCode) {
System.out.println("exit called");
}
};
assertThatThrownBy(() -> {
new Main().main(new String[] { "--help" });
}).isInstanceOf(RuntimeException.class);
assertThat(consoleOut.toString()).startsWith("Usage: bla bla ...");
}
}
I played with this for a while attempting to get it to work but unfortunately it is not possible. There are several difficulties with mocking this function; that it is a Top Level function and that it returns Nothing. Each of these can be overcome, but what makes it impossible is that the function is inlined. Inlined kotlin functions do not produce methods in the bytecode, it is as it says on the tin, inlined. The trouble is that Mockk and other mocking libraries use bytecode instructions when mocking. Refer to this issue for more information.
Your best alternative is not to attempt to mock this function at all, but instead mock the call into the third party library you are using. After all, you should not be testing this third party code, only your own code. Better yet, maybe you should seek an alternative library. As has already been stated in the comments, a third party library should not be exiting the process, this should be left up to the client code.
Related
When I create a client with the JacksonSerializer() feature and make some API calls, then run that script on my local machine, I get no error and the script runs successfully. However, when I upload this script as an AWS Lambda, I get the following error:
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of kotlin.coroutines.Continuation, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
At first, I thought the error originated from me constructing the client outside of the Handler class, but when I made the client a private value inside the Handler class, I still get the error. I've included println() statements in my function, but they don't even run. That tells me that my handleRequest() funciton isn't getting run. Is there some AWS/Lambda'ism that prevents me from using the JacksonSerializer() feature as? If so, are there any alternatives on how to parse JSON responses with the Ktor client?
My client construction:
private val client = HttpClient(Apache) {
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
An example call using the client:
val response = client.post<JsonNode> {
url(URL(GITHUB_GRAPHQL_ENDPOINT))
body = reqBody
headers {
append("Authorization", "bearer $token")
}
}
I'm guessing that you made your handler function be a kotlin suspend function? If so, that's your problem.
When you mark a function suspend, the compiler applies a bunch of magic. Most of the time, you don't need to know anything about this, other than the fact that any suspend function gets an extra parameter of type kotlin.coroutines.Continuation added to its signature. You usually don't notice this, since the compiler also makes calls to the function pass along their own hidden Continuation parameter.
Continuation, by design, can't be created by a tool like Jackson - it's an internal thing. What you probably need to do (assuming that you did make your handler function suspend) is to wrap your method in runBlocking {} and make it not be a suspend function. It's probably easiest to create a new handler, like so:
fun fixedHandler(input: MyInput, context: Context) = runBlocking {
originalHandler(input, context)
}
suspend fun originalHandler(input: MyInput, context: Context): MyOutput {
TODO("This is your original code")
}
PS - I've usually found it best to leverage the pre-defined Lambda interfaces to write my Lambda functions - it prevents you from encountering issues like this. See https://docs.aws.amazon.com/lambda/latest/dg/java-handler-using-predefined-interfaces.html for how to do it.
Have you checked out your dependencies running locally versus your dependencies in AWS? I've had issues where locally I'm running with a version, but the version in AWS was different. That could especially explain the error about continuations... Perhaps the method signature is different in whatever version you're using?
Look especially for provided scopes in your gradle/maven pom. Those are easy places for version to be out of sync.
Java CompletableFuture<T> has a lot of async methods, static or instance, in this format
public <U> CompletableFuture<U> XXXasync(SomeFunctionalInterface<T> something, Executor executor)
If you have enough experience with FP in kotlin, you will immediately realize these function are extremely awkward to use in kotlin, because the SAM interface is not the last parameter.
aCompletableFutrue.thenComposeAsync(Function<SomeType, CompletableFuture<SomeOtherType>> {
// ^ WHAT A LONG TYPE NAME THAT NEED TO BE HAND WRITTEN
// do something that has to be written in multiple lines.
// for that sake of simplicity I use convert() to represent this process
convert(it)
}, executor)
That Function has a very very long generic signature that I don't know how to let IDE generate. It will be a plain in the butt if the type name become even longer or contains a ParameterizedType or has type variance annotations.
It also looks nasty because of the trailing , executor) on line 5.
Is there some missing functionality in kotlin or IDE that can help with the situation? At least I don't want to write that long SAM constructor all by myself.
Rejected solutions:
Using named parameter doesn't seem to work because this feature only works on a kotlin function.
Abandon async methods sounds bad from the very beginning.
Kotlin corountine is rejected because we are working with some silly Java libraries that accept CompletionStage only.
IF you calling the api from java that takes a functional interface parameter at last, you can just using lambda in kotlin.
val composed: CompletableFuture<String> = aCompletableFutrue.thenComposeAsync {
CompletableFuture.supplyAsync { it.toString() }
};
Secondly, if you don't like the java api method signature. you can write your own extension methods, for example:
fun <T, U> CompletableFuture<T>.thenComposeAsync(executor: Executor
, mapping: Function1<in T, out CompletionStage<U>>): CompletableFuture<U> {
return thenComposeAsync(Function<T,CompletionStage<U>>{mapping(it)}, executor)
}
THEN you can makes the lambda along the method.
aCompletableFutrue.thenComposeAsync(executor){
// do working
}
Can I have serializable lambda in Kotlin? I am trying to use Jinq library from Kotlin, but it requires serializable lambdas. Is there any syntax that makes it possible?
Update:
My code:
var temp=anyDao.streamAll(Task::class.java)
.where<Exception,Task> { t->t.taskStatus== TaskStatus.accepted }
.collect(Collectors.toList<Task>());
I am getting this error:
Caused by: java.lang.IllegalArgumentException:
Could not extract code from lambda.
This error sometimes occurs because your lambda references objects that aren't Serializable.
All objects referenced in lambda are serializable (code results in no errors in java).
Update 2
After debugging it seems that kotlin lambda isn't translated into java.lang.invoke.SerializedLambda which is required by Jinq to get information from. So the problem is how to convert it to SerializedLambda.
I'm the maker of Jinq. I haven't had the time to look at Kotlin-support, but based on your description, I'm assuming that Kotlin compiles its lambdas into actual classes or something else. As such, Jinq would probably need some special code for cracking open Kotlin lambdas, and it may also need special code for handling any unusual Kotlin-isms in the generated code. Jinq should be capable of handling it because it was previously retrofitted to handle Scala lambdas.
If you file an issue in the Jinq github about it, along with a small Kotlin example (in both source and .class file form), then I can take a quick peek at what might be involved. If it's small, I can make those changes. Unfortunately, if it looks like a lot of work, I don't think I can really justify putting a lot of resources into adding Kotlin support to Jinq.
I have no experience on Jinq, but according to the implementation in GitHub and my experience of using Java Library in Kotlin.
ref: https://github.com/my2iu/Jinq/blob/master/api/src/org/jinq/orm/stream/JinqStream.java
You can always fall back to use the native Java Interface in Kotlin.
var temp = anyDao.streamAll(Task::class.java)
.where( JinqStream.Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
// Alternatively, You you can import the interface first
import org.jinq.orm.stream.JinqStream.*
...
// then you can use Where instead of JinqStream.Where
var temp = anyDao.streamAll(Task::class.java)
.where(Where<Task,Exception> { t -> t.taskStatus == TaskStatus.accepted } )
.collect(Collectors.toList<Task>());
Or make a custom extension to wrap the implementation
fun JinqStream<T>.where(f: (T) -> Boolean): JinqStream<T> {
return this.where(JinqStream.Where<T,Exception> { f(it) })
}
Disclaimer: The above codes have not been tested.
I'm migrating some test cases from JMock to JMockit. It's been a pleasant journey so far but there's one feature from JMock that I'm not able to find in JMockit (version 0.999.17)
I want to check that a mock is never called (any method).
With JMock, all I needed is the following in my Expectations block:
never(mock)
Is it feasible somehow with JMockit?
EDIT:
I might have found a solution but it's not very explicit.
If I put any method of this mock with times =0 in my Expectations block then this mock becomes strict and I believe any method called would trigger an exception.
Try an empty full verification block, it should verify that no invocations occurred on any given mocks:
#Test
public void someTest(#Mocked SomeType mock)
{
// Record expectations on other mocked types...
// Exercise the tested code...
new FullVerifications(mock) {};
}
Background info
I have a query regarding a questions from Sierra & Bates, SCJP v6 book. Namely Chapter 2 question 2. The answer given is that the "compilation fails". However when I tried this in neBeans, the code compiled and ran without error. It also returned a output of "D" which was not one of the alternatives. There are some other discussions on this same question in various forums, regarding the need to insert super() etc. However none seem to have recognised it can compile.
Question
1. I expected the constructor "Bottom2(String s)...to have called the super constructor "Top(String s)...". In which case the output would have been "BD" (which happens to be an option for the question. Why does the "Top(String s)..." not get called.
2. As there is a Top constructor then would the default compiler constructor still be implicitly created. ie in effect a "Top() {}" constructor which can be called by "Bottom2(String s)". This not how I understood this to happen - ie the compiler only creates this default if no other constructor is created.
3. Is there and error in this question, or is this a carry over question from the Java 5 version and somehow in Java 6 the compiler can now handle this.
4. Could netBeans have a means to "solve" the compiler problem. This is quite important as I am studying for the SCJP and I find not all the questions can be duplicated in netBeans. In which case I may learn to believe some code works when (for exam purposes) it does not.
Code included for ease of reference.
class Top {
public Top(String s) { System.out.print("B"); }
}
public class Bottom2 extends Top {
public Bottom2(String s) { System.out.print("D"); }
public static void main(String [] args) {
new Bottom2("C");
System.out.println(" ");
}
}
Top doesn't have a default constructor (a default constructor is a public constructor with empty argument list. Therefore, the constructor of Bottom2 must explicitly invoke the super constructor (and pass its argument), but doesn't, and hence compilation fails.
Indeed, eclipse helios says:
Implicit super constructor Top() is undefined. Must explicitly invoke another constructor
and javac says:
cannot find symbol
symbol : constructor Top()
location: class tools.Top
public Bottom2(String s) { System.out.print("D"); }
^
Are you really sure you have tried the same code in Netbeans?