https://kotlinlang.org logo
#spring
Title
m

marzelwidmer

05/27/2021, 6:27 PM
s

Saharath Kleips

05/27/2021, 7:15 PM
Doesn’t exactly answer your question, but MockWebServer works pretty well for a lightweight integration test.
👍 1
m

marzelwidmer

05/27/2021, 7:35 PM
I saw @sdeleuze use also
mockk
https://github.com/spring-projects/spring-framework/blob/main/spring-webflux/src/t[…]mework/web/reactive/function/client/WebClientExtensionsTests.kt when I compare it with my code I have to refactor I think… 🙂 I think I have again over myBook… 🙂 but thanks… to all…
💯 1
Have somebody any hint what this message try to tell me 🙂 when I use
.awaitBody
When I am not wrong is this the
corutine
way of
.bodyToMono
Copy code
io.mockk.MockKException: no answer found for: ResponseSpec(#4).bodyToMono(ParameterizedTypeReference<class
t

thanksforallthefish

05/28/2021, 6:49 AM
I am not familiar with reactive API, but my bet is
awaitBody<MyResponse>()
is an extension function and I think you cannot really mock those, as they don’t exist at runtime. try to keep what you had before
Copy code
coEvery { responseSpecMock.bodyToMono(MyResponse::class.java).hint(MyResponse::class) } returns response
what the error is telling is that you have a mock
ResponseSpec(#4)
on which you are calling a method
bodyToMono
with a param, but you are not recording any answer for that interaction
m

marzelwidmer

05/28/2021, 9:49 PM
I change my setup now with
exchangeFunction
: 😞 but the WebClient stuff looks more need for me…
Copy code
@Test
    fun `Should give back deckungGueltigAb value from lesefamileinvorstand`() {
        val validiereFamilienvorstandResponseJson = File("src/test/resources/scenarios/validiereFamilienvorstandResponse.json").readText()
        val webClient: WebClient = WebClient.builder().exchangeFunction {
            Mono.just(
                ClientResponse.create(HttpStatus.OK)
                    .header("Content-Type", "application/json")
                    .body(validiereFamilienvorstandResponseJson).build()
            )
        }.build()

        //  Service
        val service = LeseFamilienvorstandService(webClient = webClient, serviceConfigurer = serviceConfigurer)
        runBlocking {
            StepVerifier
                .create(service.validierePartnerCo(request).toMono())
                .expectNext(response)
                .expectComplete()
                .verify()
        }
    }

    @Test
    fun `Should throw exception NOT_FOUND (404)`() {
        val service = LeseFamilienvorstandService(webClient = webClientResponse404, serviceConfigurer = serviceConfigurer)
        StepVerifier
            .create(service.validierePartner(request))
            .expectErrorMessage(EXCEPTION_MESSAGE_NOT_FOUND_404)
            .verify()
    }

    @Test
    fun `Should throw exception INTERNAL_SERVER_ERROR (500)`() {
        val service = LeseFamilienvorstandService(webClient = webClientResponse500, serviceConfigurer = serviceConfigurer)
        StepVerifier
            .create(service.validierePartner(request))
            .expectErrorMessage(EXCEPTION_MESSAGE_INTERNAL_SERVER_ERROR_500)
            .verify()
    }
BUT the sad part with Exception Test and
coroutine
I get again some other issues….
Copy code
suspend fun validierePartnerCo(request: ValidiereFamilienvorstandRequest): ValidiereFamilienvorstandResponse {
        with(webClient) {
            return post()
                .uri(apiEndPoint())
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(request)
                .retrieve()
                .onStatus(httpStatusIs409ClientError()) { response -> Mono.error(PortalkontoBereitsVorhandenException()) }
                .onStatus(httpStatusIs404ClientError()) { response -> Mono.error(PartnerNotFoundException()) }
                .onStatus(HttpStatus::is4xxClientError) { response -> Mono.error(BadRequestException()) }
                .onStatus(HttpStatus::is5xxServerError) { response -> Mono.error(UnexpectedServiceResponseException()) }
                .bodyToMono(ValidiereFamilienvorstandResponse::class.java)
                .awaitSingle()
                .also {
                    logInfo(request, it)
                }
        }
    }

    fun validierePartner(request: ValidiereFamilienvorstandRequest): Mono<ValidiereFamilienvorstandResponse> {
        with(webClient) {
            return post()
                .uri(apiEndPoint())
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(request)
                .retrieve()
                .onStatus(httpStatusIs409ClientError()) { response -> Mono.error(PortalkontoBereitsVorhandenException()) }
                .onStatus(httpStatusIs404ClientError()) { response -> Mono.error(PartnerNotFoundException()) }
                .onStatus(HttpStatus::is4xxClientError) { response -> Mono.error(BadRequestException()) }
                .onStatus(HttpStatus::is5xxServerError) { response -> Mono.error(UnexpectedServiceResponseException()) }
//                .awaitBody<ValidiereFamilienvorstandResponse>()
                .bodyToMono(ValidiereFamilienvorstandResponse::class.java)
//                .awaitSingle()
        }
    }
I will update later to kotlin 1.5.
No look 😞 when I use the
suspend
version of my impl. mytests fail.
Copy code
runBlocking {
    StepVerifier
        .create(service.validierePartnerCo(request).toMono())
        .expectErrorMessage(EXCEPTION_MESSAGE_NOT_FOUND_404)
        .verify()
}
at |b|b|b(Coroutine boundary.|b(|b)
Can it be I impl. my
suspend
version wrong ? Or is there something mit my
StepVerifier
I have to do in an other way ? my impl. look for my correct… 😞 and I have only a problem to test my exception stuff …. or I miss undertand the
awaitSingle()
or the mix with
coroutine
and
StepVerifier
is something.. not crisp 🙂
image.png
e

Emil Kantis

05/29/2021, 9:45 AM
We gave up mocking with WebClient. The API isn't suited for testing like that at all. As someone previously suggested, we also moved to use
MockWebServer
. Just my two cents 🙂
👍 2
m

marzelwidmer

05/29/2021, 12:01 PM
I think I give it a try. normally I use the ContractsStubs but this is a bit slow:(. I will now setup also A mockWebServer. ;).
IMHO
It have issues how I use the
StepVerifier
together with
coroutine
I changed now to
Junit5
exception checks… but I have to check the
MockWebServer
still…
Copy code
@Test
    fun `Should throw exception UnexpectedServiceResponseException for 500 for partnerNr basicUser_InternalServerError`() = runBlocking<Unit> {
        shouldThrow<UnexpectedServiceResponseException> {
            val service = LeseFamilienvorstandService(webClient = webClientResponse500, serviceConfigurer = serviceConfigurer)
            service.validierePartner(request)
        }
    }
Copy code
private val webClientResponse500 = WebClient.builder()
        .exchangeFunction { Mono.just(ClientResponse.create(HttpStatus.INTERNAL_SERVER_ERROR).body(EXCEPTION_MESSAGE_INTERNAL_SERVER_ERROR_500).build()) }.build()
n

nicholasnet

05/30/2021, 2:53 PM
Just wondering instead of using WebClient can’t you use WebTestClient
🙏 1
e

Emil Kantis

05/31/2021, 6:50 AM
AFAIK it's more for testing controllers?
1
m

marzelwidmer

05/31/2021, 6:55 PM
ok…. what about ktor ?
t

Tom Hermann

06/01/2021, 1:45 PM
Mocking out a fluent API (especially with extension functions, coroutines, etc. in the mix) is a big chore. Rather than using a mock approach, i’d consider creating an integration test to cover this component. Square’s mockwebserver or wiremock work well, and give you expressive tests that are easy to read, and test error conditions a little more realistically. You can actually see how your code and webclient behave given “real” server responses, like a 500 or an invalid json body. https://www.baeldung.com/spring-mocking-webclient#mockwebserver https://dzone.com/articles/unit-tests-for-springs-webclient
🙏 1
👍 1
💯 1
73 Views