November 10, 2013
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.
September 9, 2013
What is the one key aspect of making your APIs reusable? Simply put – reducing the barriers to adoption. It matters because your target audience – architects, developers, and project managers – are all trying to deliver value to their clients. Iteration after iteration. Here are specific strategies to reduce the adoption barriers:
- Make the public API as intention revealing as possible. Looking at the public interface it must be very clear and unambiguous what the purpose and scope is of the reusable capability.
- Provide Builder or abstract factory APIs to stitch together complex object graphs in a simple fashion. Make it very easy to wire up the default behaviour and provide hooks to replace specific bits of behaviour that is varying. For example – if saving to a file is a default, provide alternative implementations or an interface hook for your client to inject a custom one.
- Make it easy to integrate the capability with other complimentary APIs. These could be in-house, open-source, or vendor APIs – key is – if they are likely to co-exist in a solution, make it simple to wire them together. If your developers use Spring for dependency injection, make your components easy to work with when it. Ditto for in-house frameworks that your teams use heavily.
- For reusable assets that perform orchestration logic provide hooks for injecting custom logic before / after your API behaviour This will be useful for initializing / cleanup logic – specially with legacy components that may need a bespoke set of steps to start them or free up resources on shutdown.
- Provide Null and/or mocking friendly implementations for testability. Your clients need to test their code via automated tests and so do you as the provider – both these needs are met via testable providers. Make it easy for developers to use them with JUnit and make it hard for them to bypass the public APIs.
August 31, 2013
Tip #31 – Inject Common Reusable Capabilities via JUnit Rules
JUnit has an extremely useful extension mechanism – Rules. The @Rule annotation can help provide additional capabilities to your test methods. For example, ContiPerf provides annotations for performance testing. Similarly, you can provide reusable framework hooks for developers to use alongside their test methods.
Some examples where this can be applied – capturing test execution metrics and publishing to a API for offline trending/analysis or setting up plumbing components to facilitate in-memory db testing via H2db, or data folders, etc.
Implementing a JUnit rule is quite straight forward – here’s an article from David Gassner that provides a sample rule implementation.