In an earlier post, I listed reasons why automated tests are foundational for reuse. In this post, want to provide some approaches that will ease automated testing of your components.
- Mock API interactions when using external dependencies. Mocking will reduce runtime dependencies and make your unit tests faster and more robust. Use JUnit with Mockito - mockito has excellent support for a variety of mocking use cases.
- If an external dependency is required from multiple classes, you can define an Adapter that will wrap the external API via an interface. The interface can then be mocked or stubbed and will provide an abstraction layer for your classes. Word of caution: abstractions are leaky and resist the need to wrap every single API provided by the external dependency.
- Use in-memory databases and provide a consistent API for your tests. A common class could initialize and clean up the in-memory db and can be leveraged from tests. Alternatively, it can be provided as an abstract class that your tests can extend. Take the opportunity to standardize location, naming, and directory structure of test resources – if you are using maven for instance, the db related data files can be placed under src/test/resources/db/<db-name>. Finally, this is very useful in ensuring that the database-bound code is indeed testable – forcing the in-memory db test will make technical debt apparent.
- Use db-deploy or some automated database deployment tooling to define and populate databases from tests – these can enable developers to define and execute tests without sharing / corrupting each other’s data. It will also make your database deployment repeatable and well tested eliminating a key deployment risk.
- Provide a common API for routinely used tasks for developers – e.g. APIs that can create test data in in-house / proprietary formats, parse, and populate appropriate data structures will be useful.
- Use JUnit Rule extensions for having a common API for developers – provide a custom rule that will manage the lifecycle of a legacy component or a API that is difficult to use – these are all opportunities to both facilitate testing and add value via reuse.