The Abstract Test Pattern (ATP)
I have received a few comments on my blog entry on Composite Unit Testing (CUT) arguying that this was the Abstract Test Pattern . Here's a snapshot of the definition from the definition form http://c2.com/cgi/:
A Testing Pattern describing a way to reuse test cases for multiple implementations of an Interface. Problem How to write a Test Suite against an Interface (or Abstract Class) that can be used to test all implementations of the interface. Solution Write an AbstractTest for every Interface and Abstract Class). The AbstractTest should have an abstract FactoryMethod that creates an object with the type of the Interface. Write a ConcreteTest for every implementation of the Interface. The ConcreteTest? should be a descendant of the AbstractTest and override the FactoryMethod to construct an instance of the implementation class.
Solution
Functional Compliance
Eric George's article gives a more detailled description of the pattern and describes it as functional compliance. It is easy enough for the compiler to tell whether a class is syntactically compliant with an interface. It applies a check to see if all required methods have been implemented with the correct signatures (syntaxic compliance), but the compiler cannot check functional compliance of a class with its interface. Here's the formal definition given by Eric George:
Functional Compliance is a module's compliance with some documented or published functional specification. The specification can be purely documentational, or it can be partially enforced through Interfaces or Abstract Classes. Interfaces and Abstract Classes along with their associated documentation represent a contract between the implementation code and the client (or user) code. It is this contract that needs to be fully tested. The Liskov Substitution Principle (LSP) tells us that all modules that honor a contract (usually by implementing an interface), should behave the same from the perspective of the client code. A module's functional compliance is really the degree to which it obey's the LSP.
So what about Composite Unit Testing ?
The remarks from the readers were right. Composite Unit Testing is
There is, however, a major difference between ATP and CUT: separation of the test code and the factory methods. In AUT, you create a ConcreteTest that inherits AbstractTest and implements a factory method, so the code that generates the tested entity is "hard-coded" into concrete test. In CUT, the framework takes care of retreiving and feeding you AbstractTest using user-specified factories (you can easily have multiple factories):
// AUT // abstract method public abstract class AbstractEnumerableTest { public IEnumerable Create(); public void GetEnumeratorTest() { IEnumerable en = this.Create(); ... } } // concrete implementation [TestFixture] public class ArrayListEnumerableTest { public override IEnumerable Create() { return new ArrayList();} }
The same test as above, using CUT:
// the fixture public class EnumerableFixture { public void GetEnumeratorTest(IEnumerable en) { ... } } // the factories public class ArrayListFactory { public ArrayList Emtpy { get{ return new ArrayList();}} } // link the fixture with the factories [CompositeFixture(typeof(EnumerableFixture), typeof(IEnumerable))] [ProviderFactory(typeof(ArraListFactory),typeof(IEnumerable))] public class EnumerableTest {}
Page rendered at Monday, October 13, 2008 11:54:02 AM UTC
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.