Skip to main content

Why Use Mock Tests?

I have been experimenting with HMock to write unit tests that use mocking, but some people question the value of mocking. Should unit tests only test pure code? Are unit tests even necessary in a language like Haskell, which has a very powerful type system?

People write unit tests for various reasons, but there are two that I find to be the most important. First, writing the tests makes the programmer think about the design and implementation of the tested code at least twice. Many bugs are found and resolved before the code goes into production. Second, unit tests provide an easy way to check assertions as the code changes. Since unit tests run quickly and (usually) do not require a complicated testing environment, they provide a very fast feedback loop that helps developers maintain code with increased confidence that they are not inadvertently breaking things.

The second benefit is much more valuable than the first, since maintenance of software is generally much more expensive than the initial development. Some people even define the term “legacy code” to mean any code that does not have sufficient tests. Making a significant change to a complex codebase that does not have tests can be very difficult, and the developer may need to take a significant amount of time to understand large parts of the system before gaining the confidence to make the change without breaking things. Tests can provide additional guides to help the developer make changes while repeatedly checking that critical assertions still hold.

The Haskell type system makes many types of bugs impossible, but that does not mean that tests are not needed. The space of valid Haskell programs may not be as large as the space of valid C programs, but it is still very large! Haskell programmers use the type system in various ways to constrain this space, but there is still a lot of logic that is generally not represented in the type system. Tests provide a way to make assertions about this logic.

When writing software, it is often a good idea to separate pure code from effectful code. It is relatively easy to write unit tests to test pure code, while testing effectful code can be difficult because care has to be taken to make the tests isolated and reproducible. Mocking provides a way to write tests for effectful code without relying on external systems or complex environments. It is very common for important business logic to be implemented in the code that call various pure functions and effectful functions, and mocks allow tests to be written to test this business logic.

People who question the value of mocking usually argue that mocks are not necessarily realistic. A mock does not test the effectful function that is being mocked, which should be tested separately, tested using integration tests, and/or tested using end-to-end tests, when possible. While this is true, it is not a valid argument against mocking, as mocking is used to test a different part of code. Note that mocking is particularly helpful in testing rare cases as well as making at least some testing possible when integration/end-to-end testing is not possible.

Writing tests incurs a cost, and one should decide what tests are worth writing (if any) according to the specific project. I suspect that many people who do not think that there is value in mock tests have simply not worked on a project where they are useful. I am investing time in HMock because I have experience with Haskell projects that would have significantly benefited from mock tests.

Author

Travis Cardwell

Published

Tags