Unit testing of value objects in isolation from its dependencies

TL DR
How do you test a value object separately from its dependencies without interruption or injection?


In a post by Misko Hevery Toward a "New" or Not a "New" ... he defends the following (cited in a blog post):

  • The Injectable class may request other Injectables constructors (sometimes I refer to Injectables as service objects, but this term is overloaded.). Injectable can never query non-Injectable (Newable) in its constructor.
  • Newables can request other constructors in its constructor, but not for Injectables (Sometimes I call Newables as Object Value, but again, this term is overloaded)

Now, if I have a Quantity value object, for example:

 class Quantity{ $quantity=0; public function __construct($quantity){ $intValidator = new Zend_Validate_Int(); if(!$intValidator->isValid($quantity)){ throw new Exception("Quantity must be an integer."); } $gtValidator = new Zend_Validate_GreaterThan(0); if(!$gtvalidator->isValid($quantity)){ throw new Exception("Quantity must be greater than zero."); } $this->quantity=$quantity; } } 

My Quantity value object depends on at least 2 validators for its proper construction. Normally, I would inject these validators through the constructor so that I can drown them out during testing.

However, according to Misko, newable should not request injections in its constructor. Honestly, a Quantity object that looks like this: $quantity=new Quantity(1,$intValidator,$gtValidator); It looks very uncomfortable.

Using the dependency injection structure to create a value object is even more inconvenient. However, now my dependencies are hardcoded in the Quantity constructor, and I cannot change them if the business logic changes.

How do you design an object of value for testing and maintaining the separation between injecting and new?

Notes:

  • This is a very simplified example. My real object, I have serious logic in it that can also use other dependencies.
  • I used the PHP example for illustration only. Responses in other languages ​​are appreciated.
+4
source share
3 answers

The Value object should contain only primitive values ​​(integers, strings, Boolean flags, other Value objects, etc.).

It would often be better if the Value object itself defended its invariants . In the example quantity that you supply, he can easily do this by checking the input value without relying on external dependencies. However, I understand that you are writing

This is a very simplified example. My real object, I have serious logic in it that can also use other dependencies.

So, while I'm going to present a solution based on an example of quantity, keep in mind that it looks too complicated because the verification logic is so simple here.

Since you also write

I used the PHP example for illustration only. Responses in other languages ​​are welcome.

I am going to answer in F #.

If you have external validation dependencies, but want to save the quantity as a value object, you need to separate the verification logic from the Value object.

One way to do this is to define an interface to test:

 type IQuantityValidator = abstract Validate : decimal -> unit 

In this case, I created a Validate template Validate the OP example, which throws exceptions when validation fails. This means that if the Validate method does not throw an exception, all this is good. For this reason, the method returns unit .

(If I had not decided to map this interface to the OP, I would rather use the Specification ; instead, I would instead declare the Validate method as decimal -> bool .)

The IQuantityValidator interface allows you to enter Composite :

 type CompositeQuantityValidator(validators : IQuantityValidator list) = interface IQuantityValidator with member this.Validate value = validators |> List.iter (fun validator -> validator.Validate value) 

This Composite simply IQuantityValidator through other instances of the IQuantityValidator and calls their Validate method. This allows you to create arbitrarily complex validator graphs.

A single sheet validator can be:

 type IntegerValidator() = interface IQuantityValidator with member this.Validate value = if value % 1m <> 0m then raise( ArgumentOutOfRangeException( "value", "Quantity must be an integer.")) 

Another may be:

 type GreaterThanValidator(boundary) = interface IQuantityValidator with member this.Validate value = if value <= boundary then raise( ArgumentOutOfRangeException( "value", "Quantity must be greater than zero.")) 

Note that the GreaterThanValidator class accepts the dependency through its constructor. In this case, the boundary is just a decimal , so it is a Primitive dependency , but it can also be a polymorphic dependence (AKA a Service).

Now you can create your own validator from these building blocks:

 let myValidator = CompositeQuantityValidator([IntegerValidator(); GreaterThanValidator(0m)]) 

When you call myValidator , for example. 9m or 42m , it returns without errors, but if you call it, for example. 9.8m , 0m or -1m it throws the corresponding exception.

If you want to create something more complex than decimal , you can enter Factory and compose Factory with the appropriate validator.

Since the quantity here is very simple, we can simply define it as an alias of type on decimal :

 type Quantity = decimal 

A Factory might look like this:

 type QuantityFactory(validator : IQuantityValidator) = member this.Create value : Quantity = validator.Validate value value 

Now you can create an instance of QuantityFactory with your selection validator:

 let factory = QuantityFactory(myValidator) 

which allows you to enter decimal values ​​as input and receive (check) Quantity values ​​as output.

These challenges succeed:

 let x = factory.Create 9m let y = factory.Create 42m 

while they raise the corresponding exceptions:

 let a = factory.Create 9.8m let b = factory.Create 0m let c = factory.Create -1m 

Now all this is very difficult , given the simple nature of the example area, but as the problem area becomes more complex, the complex is better than complex .

+2
source

Avoid value types with non-value type dependencies. Also avoid constructors that perform checks and throw exceptions. In your example, I will have a factory type that checks and creates quantities.

0
source

Your script can also be applied to objects. There are times when an object requires some dependency in order to perform any behavior. As far as I can tell, the most popular mechanism to use is double dispatch.

I will use C # for my examples.

In your case, you might have something like this:

 public void Validate(IQuantityValidator validator) 

Like other answers, the value object is usually simple enough to perform its invariant check in the constructor. An email value object will be a good example of an email that has a very specific structure.

Something more complex may be OrderLine , where we need to determine a completely hypothetical, whether, say, taxable:

 public bool IsTaxable(ITaxableService service) 

In the article you are referring to, I would say that “novelty” refers quite strongly to the “temporary” type of life cycle that we find in DI containers, as we are interested in specific cases. However, when we need to enter specific values, the transition business does not really help. This applies to objects, each of which is a new instance, but has a completely different state. A repository would moisten an object, but it could also use a factory.

True dependencies usually have a singleton life cycle.

So, for “new” instances, you can use factory if you want to perform validation during construction by specifying factory with the appropriate method of checking for your object values ​​using the introduced validation dependencies, as Mark Seemann mentioned.

This gives you the freedom to still test in isolation, without messing with the specific implementation in your constructor.

A slightly different angle on what has already been received. Hope this helps :)

0
source

All Articles