Kotlin test not found, but only sometimes - kotlin

Here is the test that I'd like to run:
#Test
fun testRequestFails() = runBlocking {
assertFailsWith(ClientRequestException::class) {
val result = httpClient.get<MyResponse>("$baseUrl/some/req/that/should/fail") {
header(API_KEY_HEADER, testApiKey)
}
}
}
httpClient.get is a suspend fun. When I try to run this with ./gradlew test or IDEA, the test is silently skipped! When I try to run this one specifically in IDEA, it says "No tests found for given includes."
However! I can make it findable and runnable by either assigning the result of assertFailsWith to a variable, or by adding assertEquals(1,1) to the end of the test.
I'm probably doing something wrong with coroutines, but in my head, if I write a test wrong, it should fail, not disappear
It's deeply troubling that a test can disappear without warning!
Is there a way to ensure the test is discoverable, without doing a useless variable assignment or a useless assert?
Is there a way to make giant alarm bells go off if a test fails to get discovered?
Snippets from build.gradle.kts:
tasks.test {
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
}
}
dependencies {
testImplementation(kotlin("test-junit5"))
testImplementation("org.junit.jupiter:junit-jupiter:5.7.0")
testImplementation("io.ktor:ktor-client-mock-jvm:$ktorVersion")
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
}

Related

Kotlin: retain coroutine context in scenario with nested runBlocking

I'm fairly new to kotlin coroutines, and I have what I think is a somewhat esoteric use case related to how runBlocking and coroutine contexts interact.
To start with, a simple example. Let's say I've got a dead simple context element. Nothing fancy.
class ExampleContext(val s: String) : AbstractCoroutineContextElement(Key) {
companion object Key : CoroutineContext.Key<ExampleContext>
}
When I run these examples, they behave exactly the way I'd expect them to:
runBlocking(ExampleContext("foo")) {
println(coroutineContext[ExampleContext.Key]?.s) // prints "foo
}
runBlocking(ExampleContext("foo")) {
launch {
println(coroutineContext[ExampleContext.Key]?.s) // prints "foo"
}
}
runBlocking(ExampleContext("foo")) {
launch(ExampleContext("bar")) {
println(coroutineContext[ExampleContext.Key]?.s) // prints "bar"
}
}
When I do this it prints null (as I would expect it to, because it runBlocking defaults to having EmptyContext in its constructor):
runBlocking(ExampleContext("foo")) {
runBlocking {
println(coroutineContext[ExampleContext.Key]?.s) // prints null
}
}
So here's my conundrum. The docs (and all the guidance I've found on the web) basically say don't do this: runBlocking is supposed to be run at the outermost layer of the coroutine logic and that's it. No nesting. What I'm working on is a library that needs to populate some context for access inside code that I don't own that gets called later (basically, you can think of it like an interceptor). The rough pseudocode looks a little like this:
class MyLibrary(otherPeoplesLogic: OtherPeoplesBusinessLogic) {
fun <IN, OUT> execute(input: IN): OUT {
... do my library's thing, including adding in a custom context element ...
try {
return otherPeoplesLogic.execute(input)
} finally {
... do my library's cleanup ...
}
}
}
To support coroutines in OtherPeoplesBusinessLogic, all I'd really have to do is add runBlocking like this:
class MyLibrary(otherPeoplesLogic: OtherPeoplesBusinessLogic) {
fun <IN, OUT> execute(input: IN): OUT {
... do my library's thing ...
runBlocking(myCustomContext) {
try {
return otherPeoplesLogic.execute(input)
} finally {
... do my library's cleanup ...
}
}
}
}
So long as all OtherPeoplesBusinessLogic::execute does is launch/async/etc, everything is fine: myCustomContext will be accessible. What I'm worried about is what happens if OtherPeoplesBusinessLogic::execute (which I'm not in control of) misbehaves and does its own runBlocking call with no context argument passed at all: what I think will happen is that myCustomContext will just silently get dropped like the example above. Not good, because it needs to be accessible.
Phew. A lot of explanation. Thanks for bearing with me. :)
So my ultimate question here is this: is there anything I can do (outside of scolding the users of my library to not call runBlocking) to prevent an accidental nested runBlocking call from dropping my context? Or am I just out of luck here and should scrap the whole idea?

vertx-web-client testing always times out, even though the assertion succeeds

Problem:
I am using the vertx-web-graphql dependency to run a graphql-server and now I want to test that said server.
But the vertx-web-client testing always times out, even after the assertion that is just before the testContext.completeNow() does not fail.
Versions:
Kotlin (1.7.20)
vertx(core, graphql, postgres, web-client, vertx-junit5) (4.3.4)
Problem-Code:
#ExtendWith(VertxExtension::class)
class GraphQlVerticleTest {
#BeforeEach
fun `deploy GraphQL Verticle`(vertx: Vertx, testContext: VertxTestContext) {
vertx.deployVerticle(GraphQlVerticle(setupPostgreSqlClient(vertx), setupJWTAuthenticationProvider(vertx)),
testContext.succeedingThenComplete())
}
#Test
fun `Wrong Login Credentials cause Login Failed message`(vertx: Vertx, testContext: VertxTestContext) {
val webClient: WebClient = WebClient.create(vertx)
testContext.assertComplete(webClient.post(graphQLPort, graphQLHostName, graphQLURI)
.putHeader("Content-Type", "application/json")
.sendJsonObject(createLoginAttemptQueryAsJsonObject(wrongEmail, wrongPassword)))
.onComplete { asyncResult ->
assertThat(asyncResult.result().bodyAsString()).isEqualTo(loginFailedResponse)
println("hello I executed")
testContext.completeNow()
}
}
#AfterEach
fun `check that the Verticle is still there`(vertx: Vertx, testContext: VertxTestContext) {
assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
}
}
The Verticle and some of the functions don't really matter, as the Assert Statement does not fail, but for some reason the testContext never gets completed and then the Test times out.
I used the testContext.assertComplete(...).onComplete(...) in all my other Vertx-Tests, but with the Web-Client it just does not work.
As far as I can tell, in this scenario the test-context has to be completed in different way, but I just can't figure out how.
Any help would be appreciated 🙂.
And please tell me if there is anything to make the question clearer, as I have never asked anything on StackOverflow before.
What I tried:
I tried placing the the assertion and testContext completion statement into a testContext.verify {} block, but that didn't work either. I read through the vertx-junit5 & vertx-web-client documentation, but there wasn't anything there concerning web-client testing. And my search didn't had any fruitful results either.
What I expected:
That the test would complete after the assertion statement succeeded, like it did in all my other vertx-tests.
Found the problem: you must complete the testContext in the method annotated with #AfterEach:
#AfterEach
fun `check that Verticle is still there`(vertx: Vertx, testContext: VertxTestContext) {
assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
testContext.completeNow()
}
Or simply not inject it:
#AfterEach
fun `check that Verticle is still there`(vertx: Vertx) {
assertThat(vertx.deploymentIDs()).isNotEmpty.hasSize(1)
}
In both cases, the test passes.
Originally posted by #tsegismont in https://github.com/eclipse-vertx/vertx-junit5/issues/122#issuecomment-1315619893

Compose Desktop testing - how to check if something is visible?

Given some simple content:
#Composable
fun MyContent() {
var showThing by remember { mutableStateOf(false) }
if (showThing) {
Box(Modifier.testTag("thing")) {
Text("The Thing")
}
}
}
If I try to test whether the thing has been displayed:
#OptIn(ExperimentalTestApi::class)
class Scratch {
#get:Rule
val compose = createComposeRule()
#Test
fun test() {
runBlocking(Dispatchers.Main) {
compose.setContent {
MyContent()
}
compose.awaitIdle()
compose.onNodeWithTag("thing").assertIsNotDisplayed()
}
}
}
I get this:
An operation is not implemented.
kotlin.NotImplementedError: An operation is not implemented.
at androidx.compose.ui.test.DesktopAssertions_desktopKt.checkIsDisplayed(DesktopAssertions.desktop.kt:23)
at androidx.compose.ui.test.AssertionsKt.assertIsNotDisplayed(Assertions.kt:49)
at Scratch$test$1.invokeSuspend(Scratch.kt:44)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
...
I thought testing whether something was displayed or not would be the most basic thing to test, but it isn't supported by the framework yet. The test framework is experimental, so I was expecting to find things missing, but not like this.
Is there another way to do this which I'm missing? All the tutorials out there talk about assertIsDisplayed() being the way, but maybe there is an alternative?
It's not a direct substitute, but unfortunately, JB Compose Desktop has these limitations in the UI test suite. Aside from using only JUnit 4, and not being compatible with the newer version, many assertion methods and also screen interaction methods are not implemented, such as the .assertIsNotDisplayed() that you tried to use, and also actions like .performTextInput().
An alternative for your problem would be using other methods like .assertDoesNotExist() and .assertExists().
It's not going to tell you if the element is in the boundaries of the screen and visible, but at least will tell you that your node exists and is instantiated, which is something, and it's better than nothing.
Until JetBrains implement the complete desktop test suite, we need to work with what we have, or maybe try implementing some things as a workaround.
In your case, this will work:
#OptIn(ExperimentalTestApi::class)
class Scratch {
#get:Rule
val compose = createComposeRule()
#Test
fun test() {
runBlocking(Dispatchers.Main) {
compose.setContent {
MyContent()
}
compose.awaitIdle()
compose.onNodeWithTag("thing").assertDoesNotExist()
}
}

Is Kotlin's runCatching..also equivalent to try..finally?

I want to run cleanup code after a certain block of code completes, regardless of exceptions. This is not a closeable resource and I cannot use try-with-resources (or Kotlin's use).
In Java, I could do the following:
try {
// ... Run some code
} catch(Exception ex) {
// ... Handle exception
} finally {
// ... Cleanup code
}
Is the following Kotlin code equivalent?
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.onFailure {
// ... Handle exception
}
Edit: added boilerplate exception handling - my concern is with ensuring the cleanup code runs, and maintainability.
There is one important difference, where the code inside runCatching contains an early return. A finally block will be executed even after a return, whereas also has no such magic.
This code, when run, will print nothing:
fun test1()
runCatching {
return
}.also {
println("test1")
}
}
This code, when run, will print "test2":
fun test2() {
try {
return
} finally {
println("test2")
}
}
There is one big difference between both code samples. try...finally propagates exceptions while runCatching().also() catches/consumes them. To make it similar you would have to throw the result at the end:
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.getOrThrow()
But still, it is not really 1:1 equivalent. It catches all exceptions just to rethrow them. For this reason, it is probably less performant than simple try...finally.
Also, I think this is less clear for the reader. try...finally is a standard way of dealing with exceptions. By using runCatching() just to immediately rethrow, you actually confuse people reading this code later.
Your question sounded a little like you believed Kotlin does not have try...finally and you need to search for alternatives. If this is the case, then of course Kotlin has try...finally and I think you should use it instead of runCatching().
As per Kotlin's doc for runCatching:
Calls the specified function block and returns its encapsulated result if invocation was successful, catching any Throwable exception that was thrown from the block function execution and encapsulating it as a failure.
Even if finally always runs after a try block and also always runs after a runCatching, they do not serve the same purpose.
finally doesn't receive any argument and cannot operate on the values of the try block, while also receives the Result of the runCatching block.
TLDR; .runCatching{}.also{} is a more advanced try{}finally{}
There is also a difference in what is the result of evaluating the expression.
Consider the following code:
fun main() {
val foo = try {
throw Exception("try")
} catch(e: Exception) {
"catch"
} finally {
"finally"
}
val bar = runCatching{
throw Exception("runCatching")
}.also{
"also"
}.onFailure {
"onFailure"
}
println(foo)
println(bar)
}
The output will be:
catch
Failure(java.lang.Exception: runCatching)
https://pl.kotl.in/a0aByS5l1
EDIT:
An interesting article that points out some differences as well:
https://medium.com/#mattia23r/a-take-on-functional-error-handling-in-kotlin-515b67b4212b
Now let’s give a second look at the implementation of runCatching in the gist above. What does it do? It catches everything.
In this case, it goes even further: it catches all Throwables. For those not knowing, Throwable is everything that can go after a throw keyword; it has two descendants: Exceptions and Errors. We haven’t mentioned Errors so far; Errors usually represent something wrong that happened at a lower level than your business logic, something that can’t usually be recovered with a simple catch.

Unpredictable coroutines execution order?

This is what I thought:
When using coroutines you go piling up async ops and once you are done with synchronous op..call them in FIFO order..but that's not always true
In this example you get what I expected:
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
println("0")
}
Also here(with nested launch):
fun main() = runBlocking {
launch {
println("1")
}
launch {
launch {
println("3")
}
println("2")
}
println("0")
}
Now in this example with a scope builder and creating another "pile"(not the real term) the order changes but still..you get as expected
fun main() = runBlocking {
launch {
println("2")
}
// replacing launch
coroutineScope {
println("0")
}
println("1")
}
Finally..the reason of this question..example 2 with scope builder:
fun main() = runBlocking {
launch {
println("3")
}
coroutineScope {
launch {
println("1")
}
println("0")
}
println("2")
}
I get this:
0
3
1
2
Why??
Was my assumption wrong and that's not how coroutines work
If so..then how should I determine the correct order when coding
edited: I've tried running the same code on different machines and different platforms but always got the same result..also tried more complicated nesting to prove non-variability of results
And digging the documentation found that coroutines are just code transformation(as I initially thought)
Remember that even if the like to call them 'light-weight' threads they run in a single 'real' thread(note: without newSingleThreadContext)
Thus I chose to believe execution order is pre-established at compile-time and not decided at runtime
After all..I still can't anticipate the order..and that's what I want
Don't assume coroutines will be run in a specific order, the runtime will decide what's best to run when and in what order. What you may be interested in that will help is the kotlinx.coroutines documentation. It does a great job of explaining how they work and also provides some handy abstractions to help managing coroutines make more sense. I personally recommend checking out channels, jobs, and Deferred (async/await).
For example, if I wanted things done in a certain order by number, I'd use channels to ensure things arrived in the order I wanted.
runBlocking {
val channel = Channel<Int>()
launch {
for (x in 0..5) channel.send(x * x)
channel.close()
}
for (msg in channel) {
// Pretend we're doing some work with channel results
println("Message: $msg")
}
}
Hopefully that can give you more context or what coroutines are and what they're good for.