Design By Contract, writing test-friendly code, object construction and Dependency Injection putting all together best practices
Asked Answered
S

5

5

I have been trying to figure out the best practices to write test-friendly code, but more specifically the practices related to object construction. In the blue book we discovered that we should enforce invariants when creating objects to avoid the corruption of our entities, value objects, etc. with this thought in mind, Design By Contract seems like the solution to avoid the corruption of our objects, but when we follow this, we could end up writing code like this:

class Car
{
   //Constructor
   public Car(Door door, Engine engine, Wheel wheel)
   {
      Contract.Requires(door).IsNotNull("Door is required");
      Contract.Requires(engine).IsNotNull("Engine is required");
      Contract.Requires(wheel).IsNotNull("Wheel is required");
      ....
   }
   ...
   public void StartEngine()
   {
      this.engine.Start();
   }
}

Well this looks good at first sight right? It seems we are building a safe class exposing the contract required so every time a Car object is created we can know for sure that the object is "valid".

Now let's see this example from a testing-driven point of view.

I want to build test-friendly code but in order to be able to test in isolation my Car object I need to create either a mock a stub or a dummy object for each dependency just to create my object, even when perhaps I just want to test a method that only uses one of these dependencies like the StartEngine method. Following Misko Hevery philosophy of testing I'd like to write my test specifying explicitly that I do not care about the Door or Wheel objects just passing null reference to the constructor, but since I am checking for nulls, I just can't do it

This is just a small piece of code but when you are facing a real application writing tests becomes harder and harder because you have to resolve dependencies for your subject

Misko proposes that we should not abuse of null-checks in the code (which contradicts Design By Contract) because of doing it, writing tests becomes a pain, as an alternative he sais it's better to write more tests than "have just the ilussion that our code is safe just because we have null-checks everywhere"

What are your thoughts on this? How would you do it? What should be the best practice?

Schonfeld answered 14/3, 2012 at 10:18 Comment(0)
N
4

Have a look at the concept of test data builders.

You create the builder once with preconfigured data, override a property if neccessary and call Build() to get a new instance of your system under test.

Or you can have a look at the sources of the Enterprise Library. The tests contain a base class called ArrangeActAssert that provides nice support for BDD-ish tests. You implement your test setup in the Arrange method of a class derived from AAA and it will be called whenever you run a specific test.

Normally answered 14/3, 2012 at 10:56 Comment(2)
test builders seem to be exactly what I was looking forSchonfeld
This pattern is named 'Object Mother', although 'test data builder' is a bit more intuitive :-)Iolanthe
J
6

I need to create either a mock a stub or a dummy object for each dependency

This is commonly stated. But I think it is wrong. If a Car is associated with an Engine object, why not use a real Engine object when unit testing your Car class?

But, someone will declare, if you do that you are not unit testing your code; your test depends on both the Car class and the Engine class: two units, so an integration test rather than a unit test. But do those people mock the String class too? Or HashSet<String>? Of course not. The line between unit and integration testing is not so clear.

More philosophically, you can not create good mock objects in many cases. The reason is that, for most methods, the manner in which an object delegates to associated objects is undefined. Whether it does delegate, and how, is left by the contract as an implementation detail. The only requirement is that, on delegating, the method satisfies the preconditions of its delegate. In such a situation, only a fully functional (non-mock) delegate will do. If the real object checks its preconditions, failure to satisfy a precondition on delegating will cause a test failure. And debugging that test failure will be easy.

Jupiter answered 14/3, 2012 at 13:28 Comment(0)
N
4

Have a look at the concept of test data builders.

You create the builder once with preconfigured data, override a property if neccessary and call Build() to get a new instance of your system under test.

Or you can have a look at the sources of the Enterprise Library. The tests contain a base class called ArrangeActAssert that provides nice support for BDD-ish tests. You implement your test setup in the Arrange method of a class derived from AAA and it will be called whenever you run a specific test.

Normally answered 14/3, 2012 at 10:56 Comment(2)
test builders seem to be exactly what I was looking forSchonfeld
This pattern is named 'Object Mother', although 'test data builder' is a bit more intuitive :-)Iolanthe
O
1

I solve this problem in the unit tests:

My test class for the car would look like:

public sealed class CarTest
{
   public Door Door { get; set; }
   public Engine Engine { get; set; }
   public Wheel Wheel { get; set; }

   //...

   [SetUp]
   public void Setup()
   {
      this.Door = MockRepository.GenerateStub<Door>();
      //...
   }

   private Car Create()
   {
      return new Car(this.Door, this.Engine, this.Wheel);
   }
}

Now, in the test methods, I only need to specify the "interesting" objects:

public void SomeTestUsingDoors()
{
   this.Door = MockRepository.GenerateMock<Door>();
   //... - setup door

   var car = this.Create();
   //... - do testing
}
Orleans answered 14/3, 2012 at 10:28 Comment(2)
Yep I have actually used this approach which basically transfers the factory concept directly to the unit test, but now I am wondering if Design By Contract is actually a good practice over writing testsSchonfeld
In my personal opinion, both Design By Contract and unit testing can be called good practices. I think the disadvantage coming from DbC for writing unit tests is very small compared to the benefit you get when doing both.Orleans
I
1

You should consider tools to do this kind of job for you. Like AutoFixture. Essentially, it creates objects. As simple as it might sound, AutoFixture can do exactly what you need here - instantiate object with some parameters that you don't care about:

MyClass sut = fixture.CreateAnnonymous<MyClass>();

MyClass will be created with dummy values for constructor parameters, properties and so on (note that those won't be default values like null, but actual instances - yet it boils down to the same thing; faked, irrelevant values that need to be there).

Edit: To extend the introduction a little bit...

AutoFixure also comes with AutoMoq extension to become full-blown auto-mocking container. When AutoFixture fails to create an object (namely, interface or abstract class), it delegates creation to Moq - which will create mocks instead.

So, if you had class with constructor signature like this:

public ComplexType(IDependency d, ICollaborator c, IProvider p)

Your test setup in scenario when you don't care about any dependencies and want just nulls, would consist entirely of 2 lines of code:

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var testedClass = fixture.CreateAnonymous<ComplexType>();

That's all there is. The testedClass will be created with mocks generated by Moq under the hood. Note that testedClass is not a mock - it's real object you can test just as if you have created it with constructor.

It gets even better. What if you want some mocks to be created dynamically by AutoFixture-Moq but some other mocks you want to have more control over, eg. to verify in given test? All you need is one extra line of code:

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var collaboratorMock = fixture.Freeze<Mock<ICollaborator>>();
var testedClass = fixture.CreateAnonymous<ComplexType>();

ICollaborator will be the mock you got full access to, can do .Setup, .Verify and all the related stuff. I really suggest giving AutoFixture a look - it's great library.

Izmir answered 14/3, 2012 at 11:28 Comment(2)
Moq is my choise (I love it btw) but still I'd like to find a cleaner way to do itSchonfeld
@Jupaol: I may not have introduced AutoFixture properly. It's not a mocking framework. You can use it together with Moq to utilize it as auto-mocking container. See my edit.Izmir
I
0

I know not everybody agrees with me (I know Mark Seemann will disagree with me), but I generally don't do null checks in my constructors for types that are created by the container using constructor injection. For two reasons, first of all it (sometimes) complicates testing -as you already noticed-. But besides that, it just adds more noise to the code. All DI containers (that I know of) will not allow null references to be injected into constructors, so there is no need for me to complicate my code for something that will not occur anyway.

Of course you could argue that because I leave null checks out for my service types, those types now implicitly know about the existence of an DI container, but this is something I can live with for my applications. When designing an reusable framework, things are different of course. In that case you will probably need all the null checks.

Iolanthe answered 14/3, 2012 at 11:42 Comment(7)
Yes...but Dependency Injection is a concept that not only apply to services (injectables) being resolved by an IoC container which means that you can use DI in a newable (entity or value object) by requiring other newables so wouldn't you check these objects either??Schonfeld
In my system I have basically two kinds of classes. 1. Data objects such as command and entities, which only contain data, and no behavior, and contain no dependencies to services. 2. Services that contain business logic and often contain other depedencencies to other services and possibly create those data objects (often by using the new operator). So I don't inject dependencies into my entities.Iolanthe
Dependency injection is indeed a pattern that can be used without a DI container. If you're not using a container, I think it will be good to include the null checks. However, for any reasonably sized system, that is designed around dependency injection, it is hard to work without a container. Since I know I that use a container, and I know which types my container will resolve for me, I allow leaving null checks out. This is just a pragmatic choice, which works very well and simplifies unit testing.Iolanthe
Yes containers should be used.Schonfeld
Steven in my opinion it seems you have an anemic-domain which is the worst anti-pattern of DDD because you say that your entities do not have behavior :S so they are simple DTO's o.O and the BL is in services so where is your domain state? inside the services? services should be stateless. Well if you are building a data-centric app perhaps it makes senseSchonfeld
Yep, but I'm not doing DDD :-D. I put my behavior in Command Handlers, which serves me very well. Take a look at this answer from Mark Seemann: https://mcmap.net/q/238081/-why-not-use-an-ioc-container-to-resolve-dependencies-for-entities-business-objects.Iolanthe
And if you want to learn more about applying command handlers, take a look at this article: cuttingedge.it/blogs/steven/pivot/entry.php?id=91.Iolanthe

© 2022 - 2024 — McMap. All rights reserved.