When working with a legacy Restlet 1.1.x application, it may become difficult to establish automated tests because no dependency injection is in place.
By getting rid of statically instantiated classes and using integration testing, it should become easier to reason about the application, reducing its overall complexity and improving its architecture.
While Restlet 2.x comes with an extension for using Google Guice for dependency
injection
in Resource
s, Restlet version 1.x comes with no such functionality
out of the box.
The approach should be very defensive and should allow minimal effort for integrating DI to the existing application because of missing test coverage.
Production call chain
The Guice dependency module is instantiated very early in the application
lifecycle in the class holding the main
method and passed to the Restlet
application class. See App.
The Restlet application uses a derived Restlet Router
to hook into the
instantiation of attached Resources
by using a derived Restlet Finder
. Both
derived components are "bridging" Guice to Restlet, to make use of the Guice
Injector
APIs. See
ExampleRestletApplication,
GuiceRouter
and GuiceFinder.
The derived Finder
is delegating the Resource
initialization from
constructor call (as the built-in Restlet Finder
does) to the Resource#init()
method (which is additionally used by the built-in Restlet Finder
).
Note
This approach may be difficult to integrate, if the existing Resources
of the
legacy application use a complex inheritance hierarchy, when their constructors
are not code-free
or simply "do too much".
Test call chain
It's now possible to introduce an (integration) base test class which handles starting and stopping the Restlet application. An abstract method allows use-case- or test-specific dependency configuration. See IntegrationTest and HelloWorldTest.
Note
Before using this approach, make sure you understand the application's bootstrapping process. Check which additional classes and / or packages are registered. For example, application-specific exception mappers could be registered either by the class-based or package-based registration process.
This is required in order to get reliable integration tests which lean towards the production configuration of the application.
A more sophisticated approach, by using JUnit initializer tooling, would be possible. This would allow better control for arrange steps in specific test methods.
This was left out for brevity and for future exercises. ๐
- Analyse runtime impacts of derived
Finder
instantiation. - Analyse refactoring efforts on complex
Resource
inheritance hierarchies. - Inspect JUnit initializer tooling for fine-grained control for arrange steps.
- Analyse side effects to other
Restlet
components, likeFilter
etc.