My take on this is that the test class technically violates the SRP, but it doesn't violate the spirit of SRP. The alternative to self-shunting is to have a mock class separate from the test class.
With the separate mock class you might think that it's all self contained and satisfies the SRP, however the semantic coupling to the mock class's attributes is still there. So, really, we didn't achieve any meaningful separation.
Taking the example out of the PDF:
public class ScannerTest extends TestCase implements Display
{
public ScannerTest (String name) {
super (name);
}
public void testScan () {
// pass self as a display
Scanner scanner = new Scanner (this);
// scan calls displayItem on its display
scanner.scan ();
assertEquals (new Item (“Cornflakes”), lastItem);
}
// impl. of Display.displayItem ()
void displayItem (Item item) {
lastItem = item;
}
private Item lastItem;
}
Now we make a Mock:
public class DisplayMock implements Display
{
// impl. of Display.displayItem ()
void displayItem (Item item) {
lastItem = item;
}
public Item getItem() {
return lastItem;
}
private Item lastItem;
}
public class ScannerTest extends TestCase
{
public ScannerTest (String name) {
super (name);
}
public void testScan () {
// pass self as a display
DisplayMock dispMock = new DisplayMock();
Scanner scanner = new Scanner (dispMock );
// scan calls displayItem on its display
scanner.scan ();
assertEquals (new Item (“Cornflakes”), dispMock.GetItem());
}
}
In practical terms (IMHO) the higher coupling of TestClass
to DisplayMock
is a greater evil than the violation of the SRP for TestClass
. Besides, with the use mocking frameworks, this problem goes away completely.
EDIT I have just encountered a brief mention of self-shunt pattern in Robert C. Martin's excellent book Agile Principles, Patterns, and Practices in C#. Here is the snippet out of the book:
So, the guy go coined the SRP (which is talked about in great detail in the same book) has no qualms using self-shunt pattern. In light of that, I'd say you are pretty safe from the OOP (Objected Orientated Police) when using this pattern.