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
Related
I have the following project, still in development: https://github.com/TarekSaid/blotit, using Kotlin and Spring Webflux.
I was writing my unit tests with Spock (Groovy), but after some issues with testing Kotlin Coroutines and being unable to use syntactic sugar with data classes (even with #JvmOverloads), I've decided to switch to Kotest + Mockk.
My only issue now is with the handler unit tests' performance, as I have to use mockkstatic on ServerRequestExtensionsKt to mock request.awaitBodyOrNull. While the Spock specification runs in 0.072s, the equivalent Kotest test runs in 0.440s. While negligible, it could add up as I add more tests.
I was wondering if there was a better way to unit test the handler with Kotest (please note that I already use WebTestClient to run integration tests). I'll eventually add some verifications to check for service calls, etc, which will be mocked. That's why I'm testing the handler directly.
The handler itself is still very simple:
#Component
class RatingHandler {
suspend fun rate(request: ServerRequest): ServerResponse {
// DataSheet is a data class used as the request body
return request.awaitBodyOrNull(DataSheet::class)?.let {
ServerResponse.ok().buildAndAwait()
} ?: ServerResponse.badRequest().buildAndAwait()
}
}
Here's my test class:
class RatingHandlerTest : StringSpec({
val handler = RatingHandler()
val request: ServerRequest = mockk()
val sheet: DataSheet = mockk()
// tried to use beforeSpec to see if it'd make a difference
beforeSpec {
mockkStatic("org.springframework.web.reactive.function.server.ServerRequestExtensionsKt")
}
"rate should return status ok when the body is present" {
coEvery { request.awaitBodyOrNull(DataSheet::class) } returns sheet
handler.rate(request).statusCode() shouldBe HttpStatus.OK
}
"rate should return invalid request for missing sheet" {
coEvery { request.awaitBodyOrNull(DataSheet::class) } returns null
handler.rate(request).statusCode() shouldBe HttpStatus.BAD_REQUEST
}
})
Is that how I'm supposed to unit test the handler, or is there a better way?
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")
}
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()
}
}
I tried reading the docs, but it just doesn't make sense to me.
I need to make three calls to an external webserivce and log the result of each after it returns. Each webservice call is indepdentant of the responses of the others. Done synchronously, it looks like this:
fun makeWebserviceCalls(){
callOne()
callTwo()
callThree()
}
fun callOne(){
// make webservice call
// log result
}
fun callTwo(){
// make webservice call
// log result
}
fun callThree(){
// make webservice call
// log result
}
Now I just need to do that in parallel. It shouldn't be that hard, but it's just not making sense to me.
I've tried:
fun makeWebserviceCalls(){
callOne()
callTwo()
callThree()
}
fun callOne(){
launch{
// make webservice call
// log result
}
}
but that doesn't compile.
I've tried:
fun makeWebserviceCalls(){
runBlocking{
callOne()
callTwo()
callThree()
}
}
suspend fun callOne(){
launch{
// make webservice call
// log result
}
}
but that doesn't compile.
I've tried:
fun makeWebserviceCalls(){
runBlocking{
callOne()
callTwo()
callThree()
}
}
suspend fun callOne(){
withContext(Dispatchers.IO){
// make webservice call
// log result
}
}
but this can't be right, because withContext is used when you need a result returned, which I don't.
What's the right way to do what I'm trying to do?
The primary goal of Coroutines is to do efficient asynchronous operations, which is different than doing concurrent operations. Here’s an example from the official docs:
val client = HttpClient()
//Running in the main thread, start a `get` call
client.get<String>("https://example.com/some/rest/call")
//The get call will suspend and let other work happen in the main thread, and resume when the get call completes
All of the above happen on the main thread, no separate threads are spawned. A future versions of Coroutines will provide OOTB support for concurrent Coroutines on multiple threads, but the main branch has no support for that yet.
A suspend function can be executed on a different thread:
suspend fun differentThread() = withContext(Dispatchers.Default){
println("Different thread")
}
Of course, if you call the suspend function from a regular function, you’ll need to do it within runBlocking.
Other alternatives are proposed here: https://kotlinlang.org/docs/mobile/concurrency-and-coroutines.html#alternatives-to-kotlinx-coroutines. For simple use cases, CoroutineWorker is a good option.
For more details, see the official docs: https://kotlinlang.org/docs/mobile/concurrency-and-coroutines.html
If you need your makeWebserviceCalls() to wait until all requests to finish:
runBlocking(Dispatchers.IO) {
launch { callOne() }
launch { callTwo() }
launch { callThree() }
}
If you need to start them in the background and return immediately:
GlobalScope.launch(Dispatchers.IO) {
launch { callOne() }
launch { callTwo() }
launch { callThree() }
}
But you need to understand that this is not the usual way of using coroutines. Normally, your makeWebserviceCalls() function and all call* functions would be suspend functions which makes them more coroutines-friendly.
You start parallel execution from out of the coroutines context, also parallel blocks of code are blocking and in such a case I'm not sure if it makes sense to use coroutines at all. You can just start 3 background threads, it will be effectively almost the same.
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.