This page covers some common testing best practices and anti-patterns.
Best Practices
Be Test-Driven
Come up with a list of end to end, component/service and unit tests that will be needed to show that the user story, requirement or improvement works as expected. For each one:
- Write the test
- Run it to see it fail
- Provide an implementation
- Run the test until it passes, refactoring to code and/or fixing any problems as needed
- Move on to the next test
Write BDD-Style Tests
Test behaviors, not methods or lines of code.
Write each test following the Behavior-Driven Development structure, i.e., Given/When/Then. In other words make sure each test:
- First provides a clear context in which it will be run (given)
- Performs the operation to be tested (when)
- Asserts that the expected outcomes have been met (then)
Use Clear Names
Tests should have names that clearly indicate the behavior being tested. For instance, catalogReturnsMetacardIdWhenIngestSucceeds or errorIsReturnedWhenInvalidUserNameIsProvided.
Positive and Negative Testing
Always remember to tests all positive and negative test scenarios and ensure that the proper results (error code, exception, message, etc.) are returned and provided in the later case.
Assertions and Validations
Mock Dependencies
Smells
Tests Difficult to Name
Too Many Dependencies
Dependencies Difficult to Mock
Anti-Patterns
Sleeps
Sleeps in tests should be avoided as they open the door to timing issues and race conditions, slow tests down and are a major cause of test flakiness.
Some options to avoid sleeps in tests include:
- Replaced the sleep with an active wait loop (a.k.a., polling), i.e., wait until a condition has been met before moving on
- Use external synchronization, i.e., use existing class notification mechanism to know it has reached a certain state before continuing
- Refactor the code under test to eliminate concurrency
- Use external dependency calls or side-effects as synchronization points in the tests