Since the Testing section of RWO 2nd is still in WIP status , and it might be not easy to find unit test tutorial for OCaml project using Async lib, I put the example here. Let me know if I was wrong.
So, here’s the
hello.ml lib that I want to test:
let get_req simple = Deferred.return simple
let%expect_test "async resp" =
let%bind res = get_req "a" in
(libraries core async expect_test_helpers_async expect_test_helpers_core)
The result of
$ dune runtest
Done: 34/36 (jobs: 1)⏎
It seems that it works, but it looks like kind of ugly in this case. I was wondering what the best practice to test the async function?
I suppose that we should create a new folder named
test, and put a
dune in it. With the
let free () = print_endline "freeing all resources"; Lwt.return ()
let test_lwt switch () =
Lwt_switch.add_hook (Some switch) free;
Lwt.async (fun () -> failwith "All is broken");
let () =
Lwt_main.run @@ Alcotest_lwt.run "foo" [
Alcotest_lwt.test_case "one" `Quick test_lwt
It just really hard to found examples about Async.
What you’ve written there seems pretty normal to me. Maybe you could say more about what you think is ugly about it.
It can certainly be a good idea to separate tests into a different library. Some reasons for this are given here.
I agree that it’s not super easy to find examples of this sort of thing with Async specifically (though I don’t think it’s too much different from non-Async expect tests). One not-very-involved example is for cohttp_async_websocket. async_smtp has a more extensive test suite in case that’s useful.
Thanks for your share, it looks like weird to me that we should print the result and check if it’s what we expected.
I believe that this kind of unit test would be better:
let get_incr a = a + 1
let test_incr _ =
assert_equal 1 (get_incr 0)
The big advantage of this style is that the initial expected value can be populated automatically by Dune when it first runs the test. This cuts down on the upfront cost of writing each test case. For instance, see this comparison between
If you don’t like the expect test style, it’s perfectly possible to write unit-tests of Async code using something like the
Alcotest_lwt example you gave:
let test_get_req () = get_req "a" >>| Alcotest.(check string) "req a" "a"
let () =
[ ("all", [ Alcotest_async.test_case "get_req" `Quick test_get_req ]) ]);
never_returns (Scheduler.go ())
This is quite boilerplate heavy, and
alcotest-async requires some work to improve usability, but there is ongoing work to fix both of those problems.
Additionally, expect tests will catch any changes to anything that you print, whether or not that change involves a property that you would have thought to check on your own. This isn’t a silver bullet: you still have to decide what to print. And it can mean that you have to read a bunch of changes that don’t affect the correctness of your code, which in the worst case means you might be more likely to miss the changes that do matter. But I think you do get some benefit from not having to think as much about what you’d like to capture in the tests.
Along the same lines, if you like expect tests they also work just as well when you aren’t testing Async code.