I am working on an application whereby I have designed classes to fit into several groups:
- Immutable: initialised through constructors, uses copy-and-swap idiom (inc. move), can deep copy (i.e. clone), only has "getters" (not "setters") and implements comparison operators (==, !=);
- Services: stateless classes that have methods that take immutables and/or services to perform desired functions;
- Builders: factories etc to build immutables.
Unit testing on my immutables has been straightforward. I can use dependency injection through the constructors. This means I can swap in test classes to ensure I am unit testing (as opposed to integration testing). I can use the builders to construct my production objects. This element of my design I am happy with in terms of testing and production.
However, on my services I seem to only be able to keep these classes stateless and unit testable by using dependency injection via the method arguments.
For my services, an example function changes from:
virtual unsigned long foo() const override final;
...to:
virtual unsigned long foo(const ISomeInterface & dependency) const override final;
This means my service classes are testable, but then I have to now instantiate the dependencies outside of the class when using the code in production. For example:
// without dependency injection
Service service;
return service.foo();
// with dependency injection
Service service;
Dependency dependency;
return service.foo(dependency);
This has led to a large number of my service classes now needing at least 1 more argument for each class method. Note - this is the approach I am currently using in my code.
My question is this:
What alternatives do I have to this form of dependency injection that allow me to:
- unit test stateless classes (without the dependencies)
- keep these classes stateless
- reduce / hide code that instantiates the dependencies (particularly if an object has multiple dependencies)
Note - I am also performing integration tests, which tests the real dependencies between objects.