Hey,
I wrote a webservice using cohttp_eio and then wanted to test it, ideally without having to start a server listening on a network interface.
My rough idea was to craft the inputs for the request handler (HTTP verb, resource, headers, body) and then call the handler under test and finally inspect the output. Sounds easy… ![]()
FYI, a request handler in cohttp_eio is just a function with three arguments conn, request and body which returns a response.
BUT: The conn and response caused me headaches as they are private types. While I was able to just drop the conn argument from my handlers as they are not used, the response was still there and is of type:
type response = writer -> unit
with an abstract type writer
I wasn’t able to figure out how to create a writer, so I discarded my initial idea and went with starting a Cohttp_eio.Server and using Cohttp_eio.Client to send real requests. Thanks to Eio’s deterministic “scheduling” I was able to do:
let stop, stop_signal = Eio.Promise.create () in
Eio.Fiber.both
(fun () ->
start_server ~stop ())
(fun () ->
test_server ();
Eio.Promise.resolve stop_signal ())
No “ugly” polling required to wait until the server is up and running until I can start sending requests. Not sure if this approach is 100% bullet proof, but it seems to work; otherwise another Promise to signal “started” would work.
For testing request handlers, I think it would be nice to be able to “mock” a Cohttp_eio connection and writer. I am just not sure if it’s worth it.
Probably a better approach would be for me to completely hide the use of Cohttp_eio from withing my handler’s code and use my own representation of a response (and body). So instead of Cohttp_eio.Server.respond_string I’d have to return my own representation. And the body argument would become a simple function fun () -> Eio.Flow.read_all body.
I am happy to hear your view and how you would test request handlers (if you test them) ![]()