'Mockito - verify call on a matched parameter

I have the following scenario. Code I'm testing (kotlin/JVM/Spring Boot)

private val client: WebClient, // injected by Spring Boot.

// (...) 
    
client.post()
   .uri(uri)
   .body(BodyInserters.fromFormData("query", query))
   .header(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED.toString())

// (...)

client is a WebClient.

In my testing code I want to check that the client is called with uri and query matching an arbitrary predicate. I came up with this:

@MockBean private val hgWebClient: WebClient,  // injected by Spring Boot Test.

// (...)

fun mockPostResponse(
    bodyMatch: (String) -> Boolean,  // in Java it would be Predicate<String>
    uriMatch: (URI) -> Boolean,
    response: String
): WebClient.RequestBodyUriSpec {
    val uriReceiver: WebClient.RequestBodyUriSpec = mock()
 // in Java it would be "when"
    whenever(hgWebClient.post()).thenReturn(uriReceiver)
    val bodyReceiver: WebClient.RequestBodyUriSpec = mock()

 // here matching with argThat works just fine
    whenever(uriReceiver.uri(argThat<URI>(uriMatch))).thenReturn(bodyReceiver)

    val spec3: WebClient.RequestBodyUriSpec = mock()

// here I have to use "any" because
// .body(BodyInserters.fromFormData("query", argThat(bodyMatch))) won't work
        whenever(bodyReceiver.body(any<BodyInserters.FormInserter<String>>())).thenReturn(spec3) 

    val spec4: WebClient.RequestBodyUriSpec = mock()
    whenever(spec3.header(any(), any())).thenReturn(spec4)
    whenever(
            spec4.exchangeToMono(any<Function<ClientResponse, Mono<String>>>())
    ).thenAnswer {
        Mono.fromSupplier { mockHttpResponse(response) }
    }
    // return both bodyReceiver and uriReceiver
}

Later I want to verify that these methods were called, so

mockPostResponse(...)

// works just fine
verify(receivers.uriReceiver) { 1 * { this.uri(argThat(uriMatch)) }  }

// No idea what to put there so it verifies that "query" in BodyInserters.fromFormData("query", query)
// matches the "bodyMatch" predicate
verify(receivers.bodyReceiver) { 1 * { this.body(???) }  }

To sum up, the specific problem is that I don't know how to mock a method call which argument itself is a mock, on which I verify that a call to a static function was made (or I'm missing something obvious). I think it's something with BodyInserters.fromFormData being a static function (?).

How to properly mock this scenario and actually verify that arguments match predicates?



Solution 1:[1]

I've found a workaround, which isn't that bad

Create a wrapper around the static Spring Boot factory and inject that in real code.

/**
 * Wrapper around BodyInserters static factory
 */
@Service
class BodyInsertersFactory {
    fun fromFormData(name: String, value: String): BodyInserters.FormInserter<String> =
        BodyInserters.fromFormData(name, value)
}

Then in tests:

    @SpyBean private val bodyInsertersFactory: BodyInsertersFactory,

// (...)

verify(bodyInsertersFactory) { 1 * { fromFormData(any(), argThat(bodyMatch)) } }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Lutos?aw