Mokksy
Mokksy is a mock HTTP server built with Kotlin and Ktor.
Why? Wiremock does not support true SSE and streaming responses. Mokksy is here to address those limitations. It's particularly useful for integration testing LLM clients.
Core Features
- Flexibility to control server response directly via
ApplicationCall
object - Built with Kotest Assertions
- Fluent modern Kotlin DSL API
- Support for simulating streamed responses and Server-Side Events (SSE)
Basic Usage
Creating Mokksy Server
1// Create a Mokksy instance
2val mokksy = Mokksy()
3
4// Configure a response for a GET request
5mokksy.get {
6 path = beEqual("/ping")
7} respondsWith {
8 body = """{"response": "Pong"}"""
9}
10
11// Start the server
12mokksy.start()
13
14// Use the server URL in your client
15val serverUrl = mokksy.serverUrl
16
17// Stop the server when done
18mokksy.stop()
Responding with Predefined Responses
GET Request
1// given
2val expectedResponse =
3 // language=json
4 """
5 {
6 "response": "Pong"
7 }
8 """.trimIndent()
9
10mokksy.get {
11 path = beEqual("/ping")
12 containsHeader("Foo", "bar")
13} respondsWith {
14 body = expectedResponse
15}
16
17// when
18val result = client.get("/ping") {
19 headers.append("Foo", "bar")
20}
21
22// then
23assertThat(result.status).isEqualTo(HttpStatusCode.OK)
24assertThat(result.bodyAsText()).isEqualTo(expectedResponse)
POST Request
1// given
2val id = Random.nextInt()
3val expectedResponse =
4 // language=json
5 """
6 {
7 "id": "$id",
8 "name": "thing-$id"
9 }
10 """.trimIndent()
11
12mokksy.post {
13 path = beEqual("/things")
14 bodyContains("\"$id\"")
15} respondsWith {
16 body = expectedResponse
17 httpStatus = HttpStatusCode.Created
18 headers {
19 // type-safe builder style
20 append(HttpHeaders.Location, "/things/$id")
21 }
22 headers += "Foo" to "bar" // list style
23}
24
25// when
26val result =
27 client.post("/things") {
28 headers.append("Content-Type", "application/json")
29 setBody(
30 // language=json
31 """
32 {
33 "id": "$id"
34 }
35 """.trimIndent(),
36 )
37 }
38
39// then
40assertThat(result.status).isEqualTo(HttpStatusCode.Created)
41assertThat(result.bodyAsText()).isEqualTo(expectedResponse)
42assertThat(result.headers["Location"]).isEqualTo("/things/$id")
43assertThat(result.headers["Foo"]).isEqualTo("bar")
Server-Side Events (SSE) Response
Server-Side Events (SSE) is a technology that allows a server to push updates to the client over a single, long-lived HTTP connection. This enables real-time updates without requiring the client to continuously poll the server for new data.
SSE streams events in a standardized format, making it easy for clients to consume the data and handle events as they arrive. It's lightweight and efficient, particularly well-suited for applications requiring real-time updates like live notifications or feed updates.
1mokksy.post {
2 path = beEqual("/sse")
3} respondsWithSseStream {
4 flow =
5 flow {
6 delay(200.milliseconds)
7 emit(
8 ServerSentEvent(
9 data = "One",
10 ),
11 )
12 delay(50.milliseconds)
13 emit(
14 ServerSentEvent(
15 data = "Two",
16 ),
17 )
18 }
19}
20
21// when
22val result = client.post("/sse")
23
24// then
25assertThat(result.status)
26 .isEqualTo(HttpStatusCode.OK)
27assertThat(result.contentType())
28 .isEqualTo(ContentType.Text.EventStream.withCharsetIfNeeded(Charsets.UTF_8))
29assertThat(result.bodyAsText())
30 .isEqualTo("data: One\r\ndata: Two\r\n")