-
Notifications
You must be signed in to change notification settings - Fork 0
Testing
Unit testing is a software development practice where individual components of a program, called "units," are tested in isolation to ensure they perform as intended. This practice should have been called "module testing" as a modules encapsulate functionality and exposes an interface while hiding the details of its internal implementation. This abstraction is crucial because it allows developers to test the behaviour of the module without being concerned with implementation details.
Good unit tests should focus on verifying the contract established by the module's interface rather than its internal workings. Bad unit tests are tightly coupled to the implementation details, such as internal methods or SQL, such that changes to the implementation can break the tests even if the module's behaviour remains consistent. This coupling increases maintenance effort and undermines the purpose of abstraction. To avoid this, tests should be designed to validate the module's interface and expected outcomes, ensuring that they remain robust and independent of internal design changes.
I have often seen good intentions lead to bad tests, e.g. 100% unit test coverage of implementation details, including the SQL generated and the parameters passed to methods being checked. What I would consider to be "Bad tests" also occurs because of the mis-understanding of what a "unit" is, so I very often see internal classes or functions being unit tested. Remember, bad unit testing costs time and money, both in up-front time as well as on-going maintenance costs, don't do it.
Back when unit testing became popular databases were much slower due to being "spinning disks of rust", so the advice was to "mock" the database otherwise the unit tests run to slowly to be useful. This is no longer true, you get a fresh copy of a Postgres database in a little as 150 ms by using database templates, setting the Write Ahead Log to minimal and turning off fsync.
No matter how many unit tests there are, the concern of any software is does it work end-to-end, i.e. when the user does this, the outcome is that. Here the focus will be business processes that involve multiple user interactions. Focus first on success (happy paths), then on key failure paths.
There is good advice on what makes a good or bad test from the Concordion FAQ page.
- supertest for web pages or web APIs (NodeJS)
- SpecFlow for C#
- Concordian for C# and Java
- A few years ago I used Fitnesse for wiki-like tests
- playwright for concurrent browser-based testing (NodeJS, Python, Java, .NET)
I see Mocks massively overused. Mocks can be useful, but typically should only be used for Mocking major external dependencies, e.g. a payment API, or a very expensive API call. If you are not mocking something like that, then don't use a mock, test the module including the dependency.
© Chris Austin 2018-2024