Reputation: 1131
REMARK :
Let's go :
I am currently trying to implement DDD and Clean Architecture principles in a new project. However, it's been 1 week that I have been stuck on ---> How to write my unit test for my use case
In Clean Architecture, when we create a use case (or a Domain service in DDD), it will depend in most cases on a certain number of entities + the rest (repository, api ...)
To write my unit test of my use case, I start with:
- Mock "the rest" of dependencies that interact with the outside (repositories, API ...)
- But next, what should I do with the entities dependencies in my unit test?
Here are the solutions I thought of :
- However, through my reading about unit test best practices, I understand that we should avoid creating mocks as much as possible because they are "Code Smells" and a good design should make it possible to do without them.
- Indeed, mocking my entities implies that I weaken my test. The test will be tightly coupled to my mocked entities.
- In addition, recreating the structure of my entities seems meaningless to me ...
* If my use case uses multiple entity methods: then I should have to recreate the return value of each of those methods.
* If the structure of my entities is complex I end up with complicated fakes to write, therefore my test loses a lot of reliability and there is a more chance that my fake is wrong, rather than my original entity)
* Even worse, if I use a factory, then I will have to make a fake of the factory -> and that fake will have to build a fake entity ...
- On the other hand, if I do not mock my entities, then I take the way in my opinion into integration tests: testing the interactions between the different entities ...
- Also as specified by some mocking supporters: If I don't mock my dependencies, then even if my tested unit is valid, the test will fail if my dependency causes a bug. This will cause a false alarm signal on my test ...
- By reading several articles some offer solutions to limit the coupling (Isolate the side effects from the rest of the logic, Isolate the logic from I/O, Use pure functions, Dependency injections, ...) But even by applying all this, a use case will irremediably need these entities to work ... Therefore it is not a solution.
But then how to do? Am I missing something?
How to do a GOOD unit test on a use case (or a service domain in DDD)? : how to manage the entities in a unit test in which the tested unit has entity depenencies ?
To illustrate my question, and to better understand your answers, here is a fictitious example of the problem:
Imagine that I have the following entity:
+class HorseEntity()
+constructor(name,age,health, ...)
+equipSaddle()
+makeShoeing()
+checkHealth()
I want to create a use case to add a horse to my stable:
+class addHorseUseCase()
+execute(requestDto,HorseRepo,HorseEntity, ...)
This usecase goes:
When I create my test, what I should to do with the "HorseEntity" dependency?
- How to write a GOOD unit test for a use case and how to handle entity dependencies in my test ?
- In general, what should I do with my entity dependencies in my unit tests?
- In this example above how to write a good unit test for "addHorseUseCase"?
Thank you for your future answers.
PS: I translate this question from French to English, if you do not understand the wording of one of my sentences do not hesitate to tell me so that I can edit it.
Upvotes: 2
Views: 1177
Reputation: 51453
You are looking for a "What makes sense?" answer, so I can only tell you what makes sense from my perspective.
First you don't need to mock all dependencies or do you mock string and array list objects in other tests?
My goal is to make my unit tests as fast as possible and easy to setup.
So to answer your questions...
How to write a GOOD unit test for a use case and how to handle entity dependencies in my test ?
Use Solution 2: I don't mock entities. I usually do that.
In general, what should I do with my entity dependencies in my unit tests?
You can mock them, but it makes your code more complicated. So just use them and make sure that they are plain objects.
In this example above how to write a good unit test for "addHorseUseCase"?
+class addHorseUseCase()
+execute(requestDto,HorseRepo,HorseEntity, ...)
HorseRepo
mock a requestDto
, a HorstEntity
and whatever you need to call the use case.EDIT
I also made the decision to not mock my entities in order to have the simplest unit test. So, don't you feel like you're doing an integration test?
It's a kind of integration testing, since you test individual software modules grouped together. But you don't integrate with external systems. I guess it's about defining what integration means to you.
So here are my 3 definitions tests:
unit: a single class's or function's api in isolation.
component: a group of classes or functions that serve one issue without external systems involved.
integration: interaction behavior with external systems - their apis.
My weighting of these different test can best be visualized with a pyramid.
/\ <-- integration
/--\
/ \ <-- component
/ \
/--------\
/ \ <-- unit
+------------+
As you can see I usually try to make as much component and unit tests as I can and I make few integration tests as needed (required). I do this to reach my goal: "I want as many tests as possible that run fast and are easy to setup."
Now the question might arise: "What about failure localization?"
E.g. When you make isolated tests of the use case and entity layer, it's easy to see where an error occured. When the tests test both in combination you can not say if the error occured in the entity or the use case layer. That's true, but you should also have unit tests for the entities. If so they might fail too and as an architectural consequense (use cases depend on entities) you have either failures in the use case and entities layer or the use case failures are a result of failures in the entities layer. Thus you should follow the architecture and first fix the entity tests since the use cases depend on them. After that you will see if any use case failures survive.
What I want to say is that isolating the use case tests by mocking the entities gives you a small advantage over component tests when it comes to failure location, but creates a lot more code to manage. So it's a tradeoff.
Upvotes: 2