0

Here is the conundrum,

I have a fairly complex Value Object and I don't want to expose it's internals. It should not be an Entity since there is no need for attaching an identity to it. According to Eric Evans in his DDD Reference book it is Ok to use a Factory in order to create complex Value Objects:

Shift the responsibility for creating instances of complex objects and aggregates to a separate object, which may itself have no responsibility in the domain model but is still part of the domain design. Provide an interface that encapsulates all complex assembly and that does not require the client to reference the concrete classes of the objects being instantiated. Create an entire aggregate as a piece, enforcing its invariants. Create a complex value object as a piece, possibly after assembling the elements with a builder.

In order to constraint the construction process, the Value Object constructor is package private and the Factory resides in the same package as the Value Object. All other classes that represent the internals of the Value object are package private and reside in the same package. The Value Object is marked as final in order to guarantee immutability. This Value Object is being used by Services that reside in a different package.

The question is, how to write unit tests for the Services that depend on this Value Object without actually using the Factory in the unit tests?

The Value Object can't be mocked considering that it has no public constructor and is final. Any thoughts or suggestions?

9
  • 2
    Why don't you want to use the Factory in unit tests?
    – bdsl
    Apr 19 at 11:45
  • @bdsl ideally, I would want my unit test to fail if something is wrong with logic in the unit I am testing, which is the Service in this case. If something is wrong with my Factory it shouldn't cause the Service unit test to fail.
    – Tetra
    Apr 19 at 11:57
  • Thanks. As VoiceOfUnreason suggests in their answer, I think it's probably not worth trying to achieve that. Just assume the Factory is reliable and deal with the failures if they happen - same as you're assuming the VO is reliable. Although may depend how awkward or complicated the factory is.
    – bdsl
    Apr 19 at 12:20
  • Yeah, agreed. Thanks for sharing.
    – Tetra
    Apr 19 at 12:56
  • Just a side note of sorts. Consider also that the package where your value object and its factory reside is a component in its own right, that needs to interact with other parts of the project - namely, other components use the value object and its factory - which means that both of these are a part of the component's API. The subset of tests that covers externally visible behavior of these two is therefore simultaneously a runnable specification of this API, and if these fail, it means that the whole app won't work. So anyone changing that package should run that subset of tests first. Apr 19 at 16:16

1 Answer 1

4

how to write unit tests for the Services that depend on this Value Object without actually using the Factory in the unit tests?

You add more layers of indirection.

If you have "services that depend on a value object", then you refactor the code so that instead you have "services that depend on an interface implemented by the value object". Then, in your test, instead of measuring the behavior of your services by passing in references to a "real" value object, you can pass in references to a test double.

For services that also depend on the factory, it's the same pattern again; you refactor the service to depend on an interface, and then use test substitutes to produce the test isolation.


BUT: before you go down that road, do a back of the envelope estimate to make sure that the benefits you expect to gain by isolating these components offset the costs.

Last time I checked, nobody is handing out prizes for "unit" tests. If testing a composition of several modules together gets the job done (ie detects mistakes in a time efficient/cost effective way), then that's fine, and you probably aren't going to realize additional benefit by redesigning to support solitary (rather than socialable) testing.

One context in which you might care a lot about isolation is when the implementation of the factory or the value object is unstable. If we're getting a lot of false positives in our service tests because the expected behaviors of the value object keep changing -- that's bad, and some form of mitigation (perhaps the one described above) will be appropriate.

1
  • yep, makes sense. I was missing the indirection as a potential solution, but I will keep using the factory within tests considering the second part of the answer. Thanks for the insight. It helped a lot.
    – Tetra
    Apr 19 at 12:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.