Wednesday, January 16, 2008

Interaction Testing of Service Collaborations

I decided to try to follow true interaction testing guidelines and use it as a design technique, specifically a technique to elicit roles and ISP compliant interfaces for DDD SERVICES.

At the minute I'm looking at how we can track changes to our domain objects so that we can report them (where appropriate) to external systems. We've decided to try using PostSharp, applying attributes to classes/members and the attributes will then cause the appropriate code to be inserted.

Interaction Test Driving The Initial Design
In my experience starting a large piece of work like this can be tricky. I've done some prototypes on using PostSharp and we basically know how we want the design to proceed.

To get me started though I need to write a high level test of this form, I decided to make it an interaction test and use TypeMock:

    [TestMethod]
    public void CanTrackCustomerActivation()
    {
        Customer customer = CustomerObjectMother.CreateProspect();
 
        DomainChangeMessage expectedMessage = new DomainChangeMessage(customer,
        "CustomerActivated");
 
        #region Mocking
 
        Mock serviceMock = MockManager.MockObject(typeof(IChangeTrackingService));
        serviceMock.ExpectCall("EventOccurred").Args(expectedMessage);
 
        IChangeTrackingService service = (IChangeTrackingService)serviceMock.MockedInstance;
 
        Mock serviceLocatorMock = MockManager.Mock(typeof(ServiceLocator), Constructor.Mocked);
        serviceLocatorMock.ExpectAndReturn("GetChangeTrackingService", service);
 
        #endregion
 
        customer.Activate();
 
        MockManager.Verify();
    } 

This test shows the following:
  1. We'll have a service of type IChangeTrackingService and we expect its EventOccurred method to be called.

  2. We expect the service to be retrieved from the existing ServiceLocator class (we considered using IoC but for now this will do, YAGNI).

Most importantly the test has helped us explore and confirm the design (see below) and although I'm not always made on interaction testing I think this one was useful.

To get the test pass all I need to do is put this sort of code into the Customer.ActivateMethod:

ServiceLocator.GetDomainChangeTrackingService().DomainEventOccurred(new DomainChangeMessage(this, "CustomerActivated"));

I'd refactor this code, but its a strarting point.

I need to make DomainChangeMessage a VALUE OBJECT to get this to pass (override Equals particularly). It was also nice that testing the changes I needed to make to DomainChangeMessage to turn it into a VALUE OBJECT (particularly overriding equals) was easy because I've written a helper class to deal with this in the past.

Importance Of The Test
In this case I'm happy that the test I created is very readable and shows clearly what the high level collaborations are.

The effect of this test on the design is subtle. I'd have designed this the same way regardless, the seperated interface (IChangeTrackingService) approach is one that works quite well and it's clear that DomainChangeMessage should be a VALUE OBJECT. However proving that this design is sound using an interaction test is valuable.

The next step is to write a test that actually checks that with a real instance of the SERVICE you get the correct behavior, which in this case might be just to save the DomainChangeMessage and make it available for use in the verification part of the test.

Oh but....
This is a bit of a cop out, every test that shows interaction testing uses it to get the role for a service style class.

What I'm really interested in is seeing how people use the same approach for domain ENTITIES, I'm not so sure that the ISP style interfaces you'd extract from them in order to get these tests to pass are so useful.

I'm prepared to be convinced though and the mock objects do seem to indicate that they think this approach can work for domain entities so I'm interested to see what they come up with.

Share This - Digg It Save to del.icio.us Stumble It! Kick It DZone

No comments:

Post a Comment