Here is a quote from Mockito unit testing framework:
Don't mock value objects
Why one would even want to do that?
Because instantiating the object is too painful !? => not a valid reason. If it's too difficult to create new fixtures, it is a sign the code may need some serious refactoring. An alternative is to create builders for your value objects -- there are tools for that, including IDE plugins, Lombok, and others. One can also create meaningful factory methods in the test classpath.
And another quote from here:
There isn't much point in writing mocks for simple value objects (which should be immutable anyway), just create an instance and use it. It's not worth creating an interface / implementation pair to control which time values are returned, just create instances with the appropriate values and use them. There are a couple of heuristics for when a class is not worth mocking. First, it has only accessors or simple methods that act on values it holds, it doesn't have any interesting behaviour. Second, you can't think of a meaningful name for the class other than VideoImpl or some such vague term.
This seems to be a valid reason for dumb value objects holding just the values and nothing more, but things getting more complicated when you have a ValueObject referencing the entities and other value objects.
Let's say I have Person and Pet objects which are entities and Relationship (owner, doctor, etc) which is a ValueObject between two persons and has a RelationshipType which is also a Value Object. So, relationship is basically:
class Relationship {
private Person person;
private Pet pet;
private RelationshipType type;
}
Now, let's say I have a class with the predicate like isOwnerRelationship, isDoctorRelationship, whatever. Basically predicate is as simple as
relationship -> relationship.isOwner(); //delegates to relationshipType.isOwner()
Now, I want to test the predicates and I have two options:
Mock Relationship
public void testIsOwner() {
Relationship rel = mock(Relationship.class);
when(rel.isOwner()).thenReturn(true);
assertTrue(RelationshipPredicates.isOwner(rel));
}
Don't mock Relationship
public void testIsOwner() {
Person person = PersonBuilder.newPerson();
Pet pet = PetBuilder.newDogPet();
RelationshipType type = RelationshipTypes.ownerType();
Relationship rel = new Relationship(person, pet, type);
assertTrue(RelationshipPredicates.isOwner(rel));
}
Of course the example is over simplified because for a person you may be required to provide address, for Pet you may have to provide BreedType, whatever, i.e. transitive graph of entities and value objects you may need to provide can be very huge. Of course you can mock Entities, but assume you have more ValueObjects of ValueObjects inside of Relationship. Even if you have fancy builders, you will have to provide each and every part of the original ValueObject even though unit test is going to test only single aspect of it.
In the predicates test, why should I care about full object construction if the predicate cares about calling one particular method of the object or combination of them?
Or is such value object can't be considered as a simple and rule doesn't apply?
Relationship
class looks like a very bad idea. In an OO domain model, relationships between entities are normally represented through associations between their classes, not with classes of their own. So, for example, aPet
would have one or more owners, while aPerson
would own zero or more pets, ie, a "many-to-many" association betweenPerson
andPet
- no extra class needed. – SabinaDate
, that's quite a complex value object no? Well it can also be trusted because you know it's been tested and should work fine. If you mock too many things then you may have to end up testing your tests because your mocks will become complex as well. Where does it stop? Just make sure complex dependencies are unit tested on their own and avoid mocks when you can. – LoganiaceousRelationship
is unit tested as well asPerson
andPet
then taking the dependency hit when testingRelationshipPredicates
shouldn't be that problematic. If yourRelationshipPredicates
fail because yourRelationship
object is not behaving it wouldn't be hard to identify because theRelationship
tests would also be failing. If you have a predicate such asisOwnerOfPetNamedRex
then your test could start withrel = ownershipOfPetNamedRexByRandomPerson()
. – Loganiaceousrel = new RelationshipBuilder().ownership().of(petNamedRex()).by(randomPerson())
. – Loganiaceous