Sunday, May 27, 2007

CAB, SCSF Testability

I came across a class the other week called TestableRootWorkItem. I was put onto it from a post I made on the Smart Client forum, and it can be found in the Appraisal Work Bench test module.

Basically it's a WorkItem which is initialised with a core set of services (of your choosing) and can then be used to test your own CAB code.

Example time - imagine you've a presenter / view combo called ShowCustomersPresenter.
The view implements an interface called IShowCustomersView.

In your test fixture you can use the TestableRootWorkItem a little bit like this:


public void MyTestOfWhatever()
{
TestableRootWorkItem rootItem = new TestableRootWorkItem();
rootItem.Services.AddNew<ICustomerService, MockCustomerService>();
ShowCustomersPresenter presenter = rootItem.Items.AddNew<ShowCustomersPresenter>();
IShowCustomerView mockView = new MockView();
presenter.View = mockView;
presenter.FindCustomers();
Assert.AreEqual(3, mockView.CustomerCount);
}


so, what's going on here then?

Well, we start with adding a mock customer service to the work item.

Next we add an instance of the Presenter we want to test. ObjectBuilder will spot a [ServiceDependency] attribute looking for a service of type ICustomerService and will add my MockCustomerService which has been registered.

The presenter's view is an IShowCustomerView. I don't want to test the real one so have added a MockView class which implements the interface. I have total control over this, but most importantly it's not a UI component.

here is my implementation of these 2 classes:


public class MockCustomerService: ICustomerService
{
public IList<CustomerSearchResult> FindCustomers(string customerName)
{
return new List<CustomerSearchResult>(new CustomerSearchResult[] { ... });
}
}

public class MockView: IShowCustomerView
{
public int CustomerCount = 0;
public ShowCustomers(IList<CustomerSearchResult> results)
{
CustomerCount = results.Count;
}
}


it's pretty plain sailing from here.
I simulate someone clicking the Find button by calling presenter.FindCustomers() directly.

My implementation of the Presenter under the hood calls the ICustomerService service to get the customers, and then calls View.ShowCustomers(results).

Using a well known service, and a mock view I can prove in the test that indeed this is what is happening.

To make it more interesting it's pretty straight forward to test Presenters raising or subscribing to events. You can create a MockEventSubscriber class which uses a CAB [EventSubscription(....)] to check for the raising of an event. The test method just has to add this class directly to the WorkItem (WorkItem.Items.AddNew<MockEventSubscriber>()) and you're there!

CABs loose coupling event plumbing makes it perfect for writing classes that can be tested in isolation. In-fact, I think that if you're not testing CAB code in this way that you're really missing a trick! I'm about to start on the next phase of a CAB project and hope to report back on how it all goes!

That's all for now :o)

No comments: