A best pratice in Object Oriented Programming is the use of Interfaces. When applied correctly it simplifies unit testing. But watch out for the pitfall of default implementation by Visual Studio. Below is an example how this can bite you.
A Document can be printed using the IPrintable interface. The implementation of the interface calls the more specific print methods based on the paper size.
public interface IPrintable { void Print(); string GetPaperSize(); } public class Document : IPrintable { public void Print() { if (GetPaperSize() == "A4") PrintOnA4(); else PrintOnDefault(); } // implementation ignored for demo purpose public string GetPaperSize() { return string.Empty; } public virtual void PrintOnA4() { } public virtual void PrintOnDefault() { } }
Now the unit test of the Print method. The test below wants to validate PrintOnA4 is called when the PaperSize is A4 and the Print method of IPrintable is called on the Document. For my unit tests I use Rhino mocks.
[TestMethod] public void Document_Print_Papersize_A4_calls_PrintOnA4() { // Arrange // PartialMock to call origional methods when no expectation is set up var testObject = MockRepository.GeneratePartialMock<Document>(); // Return PaperSize "A4" testObject.Expect(x => x.GetPaperSize()).Return("A4"); // Expect PrintOnA4 to be called testObject.Expect(x => x.PrintOnA4()); // Expect PrintOnDefault *NOT* to be called testObject.Expect(x => x.PrintOnDefault()).Repeat.Never(); // Act testObject.Print(); // Assert testObject.VerifyAllExpectations(); }
Running the test results in an InvalidOperationException because GetPaperSize is not overridable on Document.
It is part of the IPrintable interface and can be mocked there. This can be done by adding a second parameter to the GeneratePartialMock call. One mock is generated for both Document and IPrintable combined.
var testObject = MockRepository.GeneratePartialMock<Document, IPrintable>(); ((IPrintable)testObject).Expect(x => x.GetPaperSize()).Return("A4");
Now the test fails because the PrintOnDefault method is called and the PrintOnA4 is not. What happened? We mocked the GetPaperSize to return “A4”.
The method is mocked on IPrintable, but not on Document. To use the IPrintable method we need to cast to it.
public void Print() { if (((IPrintable)this).GetPaperSize() == "A4") PrintOnA4(); else PrintOnDefault(); }
Now the test Passes. But do I want to cast every time I use something from an Interface?
The solution is to implement the interface with virtual methods. That way I can mock the methods directly on the class and don’t have to cast every time. Why is this not the default in Visual Studio?
public interface IPrintable { void Print(); string GetPaperSize(); } public class Document : IPrintable { public virtual void Print() { if (GetPaperSize() == "A4") PrintOnA4(); else PrintOnDefault(); } // implementation ignored for demo purpose public virtual string GetPaperSize() { return string.Empty; } public virtual void PrintOnA4() { } public virtual void PrintOnDefault() { } }
The first unit test in this post can be used to verify that PrintOnA4 is called and not PrintOnDefault when GetPaperSize returns “A4”.