Optional GET parameters
Hi, this is a very useful library, thank you.
Could you please clarify how to handle requests with optional parameters? For example, something like this: https://site.com/path?random=123. In this case, the parameter can contain a random value, and the response should be the same regardless of what the parameter contains.
I'm using the library in a KMM project with Ktor + NetMockEngine.
Thanks in advance.
Hi @bartwell , thanks for using NetMock!
When unit testing, it's important to verify that requests are made with the correct parameters. Ignoring query parameters in your tests can lead to a situation where production code breaks, but all tests still pass. This is why NetMock requires that you specify the request parameters exactly as they will be used.
For your scenario, I recommend gaining control over the random parameter in your tests. For example, you can use an approach like this:
interface RandomGenerator {
fun generateRandom(): Int
}
class MyClassTest {
private val netMock = NetMockEngine()
private val httpClient = HttpClient(netMock) {
install(ContentNegotiation) {
json()
}
}
private val randomGenerator = object : RandomGenerator {
override fun generateRandom(): Int {
return RANDOM_NUMBER
}
}
private val sut = MyClass(httpClient, randomGenerator)
@Test
fun `EXPECT success response`() = runTest {
netMock.addMock(
request = {
requestUrl = "https://site.com/path?random=$RANDOM_NUMBER"
method = Method.Get
},
response = {
code = 200
}
)
val result = sut.doSomething()
assertTrue(result)
}
private companion object {
val RANDOM_NUMBER = Random.nextInt()
}
}
class MyClass(
private val httpClient: HttpClient,
private val randomGenerator: RandomGenerator
) {
suspend fun doSomething(): Boolean {
val response = httpClient.get(
"https://site.com/path?random=${randomGenerator.generateRandom()}"
)
return response.status.isSuccess()
}
}
Alternatively, you can set a default NetMock response and verify only the request details that matter to you afterward:
@Test
fun `EXPECT success response using default response`() = runTest {
netMock.defaultResponse = NetMockResponse(code = 200)
val result = sut.doSomething()
assertTrue(result)
val interceptedRequest = netMock.interceptedRequests[0]
assertTrue(interceptedRequest.requestUrl.contains("https://site.com/path?random="))
assertEquals(Method.Get, interceptedRequest.method)
}
However, I would discourage this approach as it doesn't fully test the request logic.
Suggested way to implement this:
Match http://localhost/users?limit=10 when requestUrl = "http://localhost/users" and expose query parameter map in response builder as follows:
response: NetMockResponseBuilder.(queryParameters: Map<String, String>) -> kotlin.Unit
An advantage to this solution is that if you don't really care about the parameter value (or presence), you don't have to mock the same url many times.
Hi @dshatz, thank you for your feedback!
In the upcoming release, I'm introducing the ability for developers to define custom matchers for intercepted requests. Here’s an example of how it can be used:
@Test
fun `EXPECT success response`() = runTest {
netMock.addMockWithCustomMatcher(
requestMatcher = { interceptedRequest ->
interceptedRequest.requestUrl.contains("https://site.com/path) && method == Method.Get
},
response = {
code = 200
}
)
val result = sut.doSomething()
assertTrue(result)
}
Do you think this will address your use case?
Hi @dshatz, thank you for your feedback!
In the upcoming release, I'm introducing the ability for developers to define custom matchers for intercepted requests. Here’s an example of how it can be used:
@Test fun `EXPECT success response`() = runTest { netMock.addMockWithCustomMatcher( requestMatcher = { interceptedRequest -> interceptedRequest.requestUrl.contains("https://site.com/path) && method == Method.Get }, response = { code = 200 } ) val result = sut.doSomething() assertTrue(result) }Do you think this will address your use case?
Looks good! Would be great to pass request information into the response building block.
@dshatz the above is now available in 0.7.0