开发者

Unit testing domain objects

We have a requirement to add an event reminder when a user enters their email address o开发者_运维技巧n an event page. Event is another domain object. Our initial thought was to create a Customer domain object and related CustomerService:

public class CustomerService {
    public void AddEventReminder(string emailAddress, int eventId) {
       var customer = new Customer(emailAddress);
       customer.AddEmailReminder(eventId);
    }
}

How can we verify in a unit test that the AddEmailReminder method was indeed called on the new customer?

My thoughts:

  1. Use a factory to create the customer. This smells because I thought you were only supposed to use factory where there was some complexity in the object creation.
  2. Bad code. Maybe there is a better way to do this?
  3. Moq magic.

On a separate note (maybe it is related), how do we decide which is the aggregate root here? We have arbitrarily decided the customer is, but it could equally be the event. I have read and understand articles on aggregate roots, but it is unclear in this scenario.


In cases like this I would create a protected method in the service that creates the customer, in the test override that method it with anonymous inner class, and make it return a mock Customer object. Then you can verify on the mock Customer object that AddEmailReminder was called. Something like:

public class CustomerService {
    public void AddEventReminder(string emailAddress, int eventId) {
       var customer = createCustomer(emailAddress);
       customer.AddEmailReminder(eventId);
    }

    protected Customer createCustomer(string emailAddress) {
       return new Customer(emailAddress);
    }
}

and in the test (assume limited C# knowledge, but it should illustrate the point):

void testCustomerCreation() {
    /* final? */ Customer mockCustomer = new Customer("email");
    CustomerService customerService = new CustomerService() {
       protected Customer createCustomer(string emailAddress) {
           return mockCustomer;
       }            
    };

    customerService.AddEventReminder("email", 14);

    assertEquals(mockCustomer.EventReminder() /* ? */, 14);
}


Thoughts on the CustomerService API

Are there any particular reasons why you have decided to encapsulate this operation in a CustomerService? This looks a little Anemic to me. Could it possibly be encapsulated directly on the Customer instead?

Perhaps you left out something of the CustomerService code example to simplify things...

However, if you must, changing the signature to take a Customer instance solves the problem:

public void AddEventReminder(Customer customer, int eventId)

but then again, an Int32 hardly qualifies as Domain Object, so the signature should really be

public void AddEventReminder(Customer customer, Event event)

The question is now whether this method adds any value at all?

Which is the aggregate root?

None of them, I would think. An aggregate root indicates that you manage children only through the root, and that wouldn't make sense either way in this case.

Consider the options:

If you make Event the root, it would mean that you could have no CustomerRepository, and the only way you could retrieve, edit and persist a Customer would be through an Event. That sounds very wrong to me.

If you make Customer the root, you can have no EventRepository, and the only way you could retrieve, edit and persist an Event would be through a specific Customer. That sounds just as wrong to me.

The only remaining possibility is that they are separate roots. This also means that they are only loosely connected to each other, and that you will need some kind of Domain Service to look up Events for a Customer, or Customers for an Event.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜