Should I test that?

Code

Which parts of your own code deserve a test: private methods, getters, logging, migrations and other small pieces.

Most asked

  1. Should I test private methods?No
  2. Should I test getters and setters?No
  3. Should I test model validations?Yes
  4. Should I test controllers?Yes
  5. Should I test trivial code?No

Yes20

  • Test the concrete methods of an abstract class once, through a small subclass that exists only in the test, and test each real subclass for the methods it implements.

  • Give each API endpoint one HTTP-level test that sends a request and checks the status code and the response fields the client reads; leave edge cases to the tests of the service code behind the endpoint.

  • Write one test that loads the configuration file of every environment through your real loader and validates it, and do not assert individual configuration values.

  • Give each controller endpoint one HTTP-level test that sends a request and checks the status code and the response body; do not unit test controller methods by calling them with a mocked service.

  • Test each business rule in a CQRS command handler by sending the command through the mediator, with an in-memory fake repository or a test database, and asserting the saved state; do not write handler tests that only check which mocked methods the handler called.

  • Test each create, read, update and delete path you write with an integration test against a real database that writes a record, reads it back and checks every field; do not unit test CRUD code against a mocked repository.

  • Write one CI test that builds the production dependency injection container and creates every registered service, and do not write a test for each registration.

  • Give each event handler one test that fires the real event on the rendered control and checks the result the user sees; do not test a handler by calling its function directly.

  • Write one unit test for each exception your code throws on purpose and for each catch block that recovers from a failure, asserting the exception type or the state the handler leaves.

  • Test a helper function that holds its own logic with direct unit tests: call it with the main input and the edge inputs most likely to break, and assert on the return value.

  • Test every input validation rule your application declares, with one accepted and one rejected input per rule plus the request bodies that real clients send; do not test that the validation library's own checks work.

  • Test an internal class that holds logic with its own unit tests, and give the test project access to it instead of making the class public.

  • Write one test that your main error path logs the failure with its request ID, and do not assert on ordinary info and debug log lines.

  • Test each validation your model declares with one valid and one invalid record; do not test that the framework's own validators, such as Rails presence or Django MaxLengthValidator, work.

  • Test the rules you write into a model, such as validations, calculated values and query scopes, by saving and reading records in a test database; do not test the field declarations that the ORM maps for you.

  • Test every hand-written regular expression that validates or parses input in a product application, with a table of strings it must match and strings it must reject, run in CI.

  • Test a public static method that contains logic with direct unit tests: call it with the main input and the edge inputs most likely to break, and assert on the return value.

  • Test every business rule in the service layer with unit tests that replace the repositories with in-memory fakes and fix the clock; do not write separate tests for service methods that only forward a call to a repository.

  • Test a value object that checks rules or computes new values with plain unit tests: each rule at its boundary, each operation, and equality between two spellings of the same value.

  • Test a void method that changes state or sends something out: call it, then assert on the effect a caller can observe, such as the saved record, the new state of the object, or the message a fake receives.

No17

  • Do not test the status text that a program prints for the person running it; unit test the values that the text reports.

  • Do not write a test that asserts the value of a constant such as a page size or an upload limit; test the behaviour that reads it, with the expected result written as a literal from the requirement.

  • Do not test a constructor that only assigns arguments of different types to fields, or one the language generates; test the methods that use those fields.

  • Do not write unit tests for a DTO that only declares fields and generated accessors; check its JSON in the test of the endpoint that sends or receives it.

  • Do not write unit tests for an enum that only lists constants stored by name; let exhaustive switches make the compiler reject a new constant, and test the code that uses the enum.

  • Do not write tests for code that a widely used generator produces from a schema or annotations; test the code that calls the generated classes, and check the schema that feeds the generator in CI.

  • Do not write tests for getters and setters that only return or assign a field; test the code that uses the values.

  • Do not write tests that assert on implementation details such as internal calls, private state or call order; test the behaviour that callers can observe through the public interface.

  • Do not repeat the tests of an inherited method in every subclass; test the method once in the class that defines it, and test each subclass only for the methods it adds or overrides.

  • Do not write tests for an interface that only declares methods; test each class that implements it, with one shared contract test when several classes implement the same interface.

  • Do not write a unit test for a method that only forwards one value, or values of different types, to another method; test the code that calls it.

  • Do not write a test for a plain belongs_to, has_many or ForeignKey declaration; test the association options that decide what a delete removes or which rows come back.

  • Do not write tests that call private methods directly; test their behaviour through the public methods that call them.

  • Do not write tests that call protected methods directly; test their behaviour through the public methods of the base class and its subclasses.

  • Do not test a main method that only builds objects from arguments of different types and calls one entry point; move argument parsing and exit code logic into a function that you unit test.

  • Do not write tests for a toString() that only feeds log lines and the debugger; generate it, and test it only when users, stored data or other code read its output.

  • Do not write unit tests for branchless one-line wrappers and delegations that pass one value or values of different types; test the code that calls them.