1

This question is a follow-up question to Clean Architecture use case testing.

Suppose the production code injects Use Case Interactor into the Input Boundary - that happens somewhere in the main component1. But now I want to test the Input Boundary/Output Boundary of the use case. I can only think of two ways to do this:

  1. Recreate the dependency injection setup at the beginning of the test - this forces the test to depend on Use Case Interactor.
  2. Depend on the main component for DI - this seems risky because the main component is volatile.

Is there a clean way to handle this situation?

1 Clean Architecture Chapter 26, Robert C Martin

Brady Dean
  • 113
  • 4
  • Do you want to unit test `InputBoundary` in isolation, or do you want to create an integration test for `InputBoundary` and `UseCaseInteractor`? – Doc Brown Jul 29 '21 at 16:01
  • @The latter, I want to test that boundary of the use case. – Brady Dean Jul 29 '21 at 16:03
  • You want to test `InputBoundary` which depends on `UseCaseInteractor` - so to test `InputBoundary` you must pass instance of `UseCaseInteractor`. It is ok for test to depend on input parameters (`UseCaseInteractor` is an input parameter of `InputBoundary` which passed via constructor). – Fabio Jul 30 '21 at 05:31
  • @Fabio I think I understand what you're saying, but I don't understand what you mean by "`UseCaseInteractor` is an input parameter of `InputBoundary`". `InputBoundary` is an interface, it doesn't have a constructor. I spent some more time looking at clean architecture and I think it would be best to test `UseCaseInteractor` directly, rather than throught the boundaries. – Brady Dean Jul 30 '21 at 15:59
  • 1
    @BradyDean, you said _I want to test the Input Boundary/Output Boundary of the use case_ - that mean you have an implementation of these interfaces, which takes `UseCaseInteractor` as a constructor argument (I assume) – Fabio Jul 30 '21 at 21:10
  • @Fabio Often times people describe "test harnesses" and say they drive the system. I'm interpreting this to mean the test harness takes the place of `Controller` and `Presenter`. It drives the system (use case) through `InputBoundary`, and verifies the result through the `OutputBoundary`/`Presenter`. However, in the situation where `UseCaseInteractor` must be injected I am unsure of how to write this test harness. Should the test manually instantiate `UseCaseInteractor`, or should the test require it to be injected? – Brady Dean Jul 30 '21 at 21:55
  • @Fabio I might also be misinterpreting what is meant my "test harness", and a test shouldn't cross boundaries as I previously described, but should instead target a concrete implementation such as `UseCaseInteractor`. – Brady Dean Jul 30 '21 at 21:57
  • 1
    @BradyDean, _Should the test manually instantiate UseCaseInteractor_ - did you try that? Did this approach cause any issues? You need to start with something – Fabio Jul 31 '21 at 01:02

2 Answers2

0

The input boundary is a simple abstraction to prevent the controller from knowing about the specific use case interactor. It doesn’t do anything, it simply invokes the interactor.

If your language supports generics, you can makes this a generic class. In your test, create a fake interactor and use that to test the generics.

Personally I use the Mediatr library (C#). All interactors implement an IRequestHandler interface and input data implements IRequest. Inject Mediatr into the controller, create a new request and call mediator.Send(request);. It doesn’t get much cleaner than that and because you can rely on a library you don’t even have to write and test the glue code yourself!

Rik D
  • 3,806
  • 2
  • 15
  • 26
  • I think my concern is with how coupled the tests should be to the internals of the use case. I think the test ought to be independent of the specific use case implementation, but DI forces a decision to be made before the test can execute. – Brady Dean Jul 29 '21 at 18:42
  • `specificInteractor.Handle(request)` - clean one line without dependencies on third party libraries(Mediatr) ;) – Fabio Jul 29 '21 at 20:08
0

How can I manage dependency injection in test code?

The same way you manage it in production code.

Suppose the production code injects Use Case Interactor into the Input Boundary

Then production code is confused because you don't inject classes into interfaces.

But now I want to test the Input Boundary/Output Boundary of the use case

Stop that. Test behavior not structure. The boundaries do not hold behavior. They define the mini language that can be used to communicate across the boundary. Tests should use that same mini language to test what's across the boundary.

I think it would be best to test UseCaseInteractor directly, rather than throught the boundaries. @Brady Dean

Using UseCaseInteractor through the boundary is the direct way. Anything else is sneaking past the abstraction you're meant to use. Tests that sneak in the back door only prove that the back door works.

Is there a clean way to handle this situation?

Focus on the behavior you expect when you use the unit under test in the way it's meant to be used. Don't worry about isolation unless that's needed to make the unit's behavior deterministic and fast. Don't go sneaking around abstractions. And stop treating behaviorless structure code as something that needs to be tested.

candied_orange
  • 102,279
  • 24
  • 197
  • 315
  • It's apparent I didn't ask the question clearly enough. *But now I want to test the Input Boundary/Output Boundary of the use case* should have been *But now I want to test the use case through the IO Boundaries* (which you did well describing as the "direct way"). As for the dependency injection stuff - I had in mind something like ASP.NET Core's IOC container where you ask for an `IInputBoundary` and are given a `UseCaseInteractor` object. I suppose that for the purpose of testing, these objects should just be created by hand. – Brady Dean Aug 29 '21 at 19:31