# What functions deserve tests?

**URL:** <https://discuss.ocaml.org/t/what-functions-deserve-tests/13268>\
**Category:** Learning\
**Tags:** language-agnostic\
**Created:** [October 19, 2023, 12:01am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268 "2023-10-19T00:01:55Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![unfode](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unfode/32/4576_2.png) [@unfode](https://discuss.ocaml.org/u/unfode)\
**Post date:** [October 19, 2023, 12:01am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/1 "2023-10-19T00:01:55Z")

</div>

In my opinion, tests are not needed for function `string_of_day`:

```ocaml
type day = Monday | Tuesday
let string_of_day (day: day) : string =
  match day with
  | Monday -> "Monday"
  | Tuesday -> "Tuesday"

```

In contrast, I feel obliged to write tests for a sort function.

What’s worse, I’m not sure if I should write tests for function `add1_all`:

```ocaml
let add1_all (nums: int list) : int list =
  List.map (fun n -> n + 1) nums

```

So what functions deserve tests?

My idea: Tests are used to make sure a function implementation matches its specification (aka “program correctness”). A function’s implementation may closely resemble its specification (eg, `string_of_day`), or may be very different from its specification (eg, quicksort). Little resemblance means high need to write tests, and vice versa.

* * *

Additionally, what do you think of mocking? In [Mocking is a Code Smell](https://medium.com/javascript-scene/mocking-is-a-code-smell-944a70c90a6a), the author suggests that the need to mock is a sign of bad code structure. And he wrote:

> If there is no logic in your code (just pipes and pure compositions), 0% unit test coverage might be acceptable, assuming your integration or functional test coverage is close to 100%.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [October 19, 2023, 12:31am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/2 "2023-10-19T00:31:32Z")

</div>

> [@unfode](#):
>
> So when should we write tests for a function?

Not a very good tester but I tend to write tests when there is a bug, either while developing, either after someone reported one. This means something I thought was evident wasn’t. Also edges cases are good candidates (e.g. empty strings/lists) lots of bugs lurk there.

> [@unfode](#):
>
> My idea: Tests are used to make sure a function implementation matches its specification (aka “program correctness”).

Then rather write specifications in OCaml itself and automate the test generation. [Quickcheck](https://en.wikipedia.org/wiki/QuickCheck) and [fuzzing](https://en.wikipedia.org/wiki/Fuzzing) frameworks do that for you (`opam search quickcheck`).

---

<div class="post-metadata">

**Author:** ![yawaramin](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/yawaramin/32/3384_2.png) [@yawaramin](https://discuss.ocaml.org/u/yawaramin)\
**Post date:** [October 19, 2023, 3:10am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/3 "2023-10-19T03:10:23Z")

</div>

> [@unfode](#):
>
> In my opinion, tests are not needed for function `string_of_day`:

What if the implementation has a typo and the output string is `Mondy`?

I think if the tests are really easy to write, the energy required to argue against writing them becomes greater than the energy needed to just write the tests.

---

<div class="post-metadata">

**Author:** ![unfode](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unfode/32/4576_2.png) [@unfode](https://discuss.ocaml.org/u/unfode)\
**Post date:** [October 19, 2023, 3:39am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/4 "2023-10-19T03:39:10Z")

</div>

```ocaml
let string_of_day (day: day) : string =
  match day with
  | Monday -> "Monday"
  | Tuesday -> "Tuesday"

```

Writing tests for `string_of_day` is essentially writing the function once again, which doesn’t feel right.

---

<div class="post-metadata">

**Author:** ![shonfeder](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/shonfeder/32/424_2.png) [@shonfeder](https://discuss.ocaml.org/u/shonfeder)\
**Post date:** [October 19, 2023, 3:45am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/5 "2023-10-19T03:45:16Z")

</div>

A test is a program that helps check whether another program is “correct” based on some known criteria or specification. Imo, a test should both provide this check and convince a reader that it checks what it claims to. If tests are redundant in the ways you point out, or are too complicated, then I think they fail to perform these functions.

So, I tend to avoid writing unit tests if the code for the test is not simpler than the implementation of the program i am testing.

I would not write a test for `add_1_all`. In fact, I would not write that function and just use `List.map ((+) 1)` directly. IMO, best to not add names for functions that are so obvious to read off the composition of combinators that they are built from. I would probably write a (ideally property based) test for the functions that used that tho 🙂

That said, if a colleague insisted I write a test that I thought wasn’t worth it, I’d probably do that instead of argue about it.

---

<div class="post-metadata">

**Author:** ![unfode](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unfode/32/4576_2.png) [@unfode](https://discuss.ocaml.org/u/unfode)\
**Post date:** [October 19, 2023, 3:47am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/6 "2023-10-19T03:47:24Z")

</div>

> [@shonfeder](#):
>
> avoid writing unit tests if the code for the test is not simpler than the implementation of the program i am testing

Seems like a good standard!

---

<div class="post-metadata">

**Author:** ![olleharstedt](https://avatars.discourse-cdn.com/v4/letter/o/f1d935/32.png) [@olleharstedt](https://discuss.ocaml.org/u/olleharstedt)\
**Post date:** [October 19, 2023, 6:54am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/7 "2023-10-19T06:54:44Z")

</div>

What’s the impact of a failure? If it’s high, write a test. Writing automated tests is a risk mitigation technique. It can also be a good way to document the correct, expected behaviour of a unit or system.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [October 19, 2023, 7:07am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/8 "2023-10-19T07:07:53Z")

</div>

> [@yawaramin](#):
>
> What if the implementation has a typo and the output string is `Mondy`?

_Then_ you write a test :–) I mean I wouldn’t object to someone writing a test for that upfront, I just know that for myself I wouldn’t.

To add two more things to my initial message.

If you are writing libraries handling standards a good way to test is to devise a cli tool that provides a service for the standard and then dog food yourself with tool (and/or expect test it).

Also since this is out of personal failure if you assess the correctness of some of your functions in the toplevel then just don’t. When I wrote `xmlm` 16 years ago I made a lot of tests to make sure the various options to handle the whitespace were working correctly. All these tests have now vanished which is very stupid.

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [October 19, 2023, 7:45am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/9 "2023-10-19T07:45:50Z")

</div>

> [@olleharstedt](#):
>
> What’s the impact of a failure?

How do you assess the impact of a failure ?

We perfectly know by now that in a system, a small “innocuous” bug in a component can lead to catastrophic failure of the whole system.

---

<div class="post-metadata">

**Author:** ![Charles](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/charles/32/4478_2.png) [@Charles](https://discuss.ocaml.org/u/Charles)\
**Post date:** [October 19, 2023, 8:24am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/10 "2023-10-19T08:24:43Z")

</div>

Perhaps one should look at this stackexchange answer

> <https://softwareengineering.stackexchange.com/questions/446061/what-is-the-point-of-unit-tests>

---

<div class="post-metadata">

**Author:** ![Release-Candidate](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/release-candidate/32/3550_2.png) [@Release-Candidate](https://discuss.ocaml.org/u/Release-Candidate)\
**Post date:** [October 19, 2023, 9:02am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/11 "2023-10-19T09:02:21Z")

</div>

> [@unfode](#):
>
> In my opinion, tests are not needed for function `string_of_day`:

I wouldn’t write one either. But if you (and you always should) add a `day_of_string`, you can do tests against identity (modulo options or errors).

---

<div class="post-metadata">

**Author:** ![benjamin-thomas](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/benjamin-thomas/32/3966_2.png) [@benjamin-thomas](https://discuss.ocaml.org/u/benjamin-thomas)\
**Post date:** [October 19, 2023, 11:45am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/12 "2023-10-19T11:45:01Z")

</div>

This is of course highly subjective but I’ll give my 2c.

I like to think of what is the cost/benefit of testing or not testing a particular piece of code.

If I’m unsure how a function will behave, testing is a net benefit because the alternative is manually testing when the code changes and this will take longer over the long term.

Inversely, I could spend time writing and maintaining tests for something which will not actually break in practice or that is not actually important. In that case it’s a net cost and I should really use my time better.

One thing that I feel TDD conversations usually sweep under the rug is the subject of design. If your design is poor, then I feel no amount of testing will actually help. Too many inputs produces too many outputs, so one mustn’t be too focused on the tests themselves at this point. Both are not mutually exclusive of course.

Another viewpoint which I find convincing is to consider testing being similar to double entry accounting : you specify things twice such that if you made a mistake on either side, something is bound to bubble up.

---

<div class="post-metadata">

**Author:** ![olleharstedt](https://avatars.discourse-cdn.com/v4/letter/o/f1d935/32.png) [@olleharstedt](https://discuss.ocaml.org/u/olleharstedt)\
**Post date:** [October 19, 2023, 1:31pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/13 "2023-10-19T13:31:59Z")

</div>

> [@dbuenzli](#):
>
> How do you assess the impact of a failure ?

Risk and assumption analysis. 🙂 Also compare with [spiral model](https://en.wikipedia.org/wiki/Spiral_model).

---

<div class="post-metadata">

**Author:** ![unfode](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/unfode/32/4576_2.png) [@unfode](https://discuss.ocaml.org/u/unfode)\
**Post date:** [October 19, 2023, 2:15pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/14 "2023-10-19T14:15:52Z")

</div>

What does “tests against identity” mean?

---

<div class="post-metadata">

**Author:** ![Release-Candidate](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/release-candidate/32/3550_2.png) [@Release-Candidate](https://discuss.ocaml.org/u/Release-Candidate)\
**Post date:** [October 19, 2023, 2:32pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/15 "2023-10-19T14:32:39Z")

</div>

Oh, sorry.

What I mean is composing two functions which are (“morally”) inverse and yield the identity function.

So, `string_of_date . date_of_string = id` (ignoring invalid strings, which should be tested too).  
and `date_of_string . string_of_date = id`.

`string_of_date (date_of_string s) = s`  
`date_of string (string_of_date d) = d`

These are tests which are perfectly fit for property testing (using QCheck/Quickcheck).

---

<div class="post-metadata">

**Author:** ![dbuenzli](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/dbuenzli/32/18_2.png) [@dbuenzli](https://discuss.ocaml.org/u/dbuenzli)\
**Post date:** [October 19, 2023, 2:38pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/16 "2023-10-19T14:38:04Z")

</div>

> [@olleharstedt](#):
>
> Risk and assumption analysis. 🙂 Also compare with [spiral model](https://en.wikipedia.org/wiki/Spiral_model).

Good luck with that :–)

You can’t assess risks and assumptions in a modular context since you don’t know how the component you are working on is going to be integrated.

So basically if you want to be safe the answer to this question:

> [@olleharstedt](#):
>
> What’s the impact of a failure? If it’s high, write a test.

Is always: high. There are no little bugs, even a display bug on a dashboard may lead a human operator to take catastrophic decisions.

---

<div class="post-metadata">

**Author:** ![olleharstedt](https://avatars.discourse-cdn.com/v4/letter/o/f1d935/32.png) [@olleharstedt](https://discuss.ocaml.org/u/olleharstedt)\
**Post date:** [October 19, 2023, 4:04pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/17 "2023-10-19T16:04:22Z")

</div>

That conclusion is only useful if money and time is infinite. But yes, risk is domain specific, of course. Risk also includes probability, not just impact.

---

<div class="post-metadata">

**Author:** ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)\
**Post date:** [October 19, 2023, 8:05pm UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/18 "2023-10-19T20:05:47Z")

</div>

Some (possibly incorrect) thoughts:

1. typically systems/libraries are inadequately tested. So if you’re down to this function (`string_of_day`), then you’re doing so much better than even most of the best devs, that you ought to feel pretty good. So there might be lower-hanging targets for you to add test coverage for ? Just a thought.

2. Mocks: I’ve most developed systems – and typically distributed systems with significant internal state. In that context, it’s important to use mocks in order to be able to induce failure-modes for subsystem B, so that we can verify that subsystem A properly handles it.

There are other examples where mocks are really useful: verifying that in a particular error-mode, a system emits a particular log-message (it might not be able to crash, but during recovery it should still emit particular alerts).

The list probably goes on and on. I think the author of that post kind of knows this: “Mocking is great for integration tests”.

---

<div class="post-metadata">

**Author:** ![fdagnat](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/fdagnat/32/88_2.png) [@fdagnat](https://discuss.ocaml.org/u/fdagnat)\
**Post date:** [October 20, 2023, 8:17am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/19 "2023-10-20T08:17:56Z")

</div>

> [@benjamin-thomas](#):
>
> Another viewpoint which I find convincing is to consider testing being similar to double entry accounting : you specify things twice such that if you made a mistake on either side, something is bound to bubble up.

That is exactly the message I use with my students reluctant to write tests. I would add that the two way of writing the same thing do not have the same lifecycle which is crucial:

1. Often, a function may evolve while the test last. The ROI coming from regression checking is sometimes not forseen or forgotten (this relates with Daniel history of xmlm above).
2. If the author of the two objects (the function code and its tests) are different, it is better.

---

<div class="post-metadata">

**Author:** ![bluddy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/bluddy/32/104_2.png) [@bluddy](https://discuss.ocaml.org/u/bluddy)\
**Post date:** [October 20, 2023, 8:55am UTC](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268/20 "2023-10-20T08:55:55Z")

</div>

My opinion is that testing needs to be based first and foremost on your interface. Whatever your public interface is, that needs to be tested first. The next candidates are high-complexity functions and functions that manipulate a lot of state. These need to be tested thoroughly to make sure you’re exercising every execution path and edge case. Otherwise you have no idea if your code works.

At the extreme end, since every part of your code can be refactored into a ‘function’, you’d then be obligated to test every single one of these functions. This clearly doesn’t make sense and it would make refactoring near impossible. So there needs to be a balance.

At some ideal level, your test code would only cover your interface and that would be ‘good enough’, allowing you to refactor without needing a test rewrite. This is too idealistic though, as there are many parts of code, particularly stateful code, that deal with a massive state space that isn’t readily replicated via the interface. But I think this is something to strive for.

[Next page](https://discuss.ocaml.org/t/what-functions-deserve-tests/13268.md?page=2)
