Sign Up |  Register

All tags

Isolation testing: fake implementations

09.09.2013739 просм.

If the component that you are developing depends on another component that someone else is developing at the same time, then you have the problem that you can’t run your component until theirs is working.

A less serious issue is that even if the other component exists, its behavior can be variable depending on its internal state, which might depend on many other things; for example, a stock market price feed varies from one minute to the next. This makes it difficult to test your component for predictable results. The solution is to isolate your component by replacing the dependency with a fake implementation. A fake simulates just enough of the real behavior to enable tests of your component to work. The terms stub, mock, and shim are sometimes used for particular kinds of fakes. The principle is that you define an interface for the dependency. You write your component so that you pass it an instance of the interface at creation time. For example:

C#
// This interface enables isolation from the stock feed:
public interface IStockFeed
{
int GetSharePrice(string company);
}
// This is the unit under test:
public class StockAnalyzer
{
private IStockFeed stockFeed;
// Constructor takes a stockfeed:
public StockAnalyzer(IStockFeed feed) { stockFeed = feed; }
// Some methods that use the stock feed:
public int GetContosoPrice() { … stockFeed.GetSharePrice(…) … }
}

By writing the component in this way, you make it possible to set it up with a fake implementation of the stock feed during testing, and a real implementation in the finished application. The key thing is that the fake and the real implementation both conform to the same interface.

This separation of one component from another is called “interface injection.” It has the benefit of making your code more flexible by reducing the dependency of one component on another. You could define FakeStockFeed as a class in the ordinary way:
C#
// In test project.
class FakeStockFeed : IStockFeed
{
public int GetSharePrice (string company) { return 1234; }
}
And then in your test, you’d set up your component with an instance of the fake:
C#
[TestClass]
public class StockAnalysisTests
{
[TestMethod]
public void ContosoPriceTest()
{
// Arrange:
var componentUnderTest = new StockAnalyzer(new FakeStockFeed());
// Act:
int actualResult = componentUnderTest.GetContosoPrice();
// Assert:
Assert.AreEqual(1234, actualResult);
}
}
However, there’s a neat mechanism called Microsoft Fakes that makes it easier to set up a fake, and reduces the clutter of the fake code.

Microsoft Fakes

If you’re using MSTest in Visual Studio 2012, you can have the stub classes generated for you. In Solution Explorer, expand the test project’s references, and select the assembly for which you want to create stubs—in this example, the Stock Feed. You can select another project in your solution, or any referenced assembly, including system assemblies. On the shortcut menu, choose Add Fakes Assembly. Then rebuild the solution. Now you can write a test like this:

C#
[TestClass]
class TestStockAnalyzer
{
[TestMethod]
public void TestContosoStockPrice()
{
// Arrange:
// Create the fake stockFeed:
IStockFeed stockFeed =
new StockAnalysis.Fakes.StubIStockFeed() // Generated by Fakes.
{
// Define each method:
// Name is original name + parameter types:
GetSharePriceString = (company) => { return 1234; }
};
// In the completed application, stockFeed would be a real one:
var componentUnderTest = new StockAnalyzer(stockFeed);
// Act:
int actualValue = componentUnderTest.GetContosoPrice();
// Assert:
Assert.AreEqual(1234, actualValue);
}

}

The special piece of magic here is the class StubIStockFeed. For every public type in the referenced assembly, the Microsoft Fakes mechanism generates a stub class. The type name is the same as the original type, with “Stub” as a prefix.

This generated class contains a delegate for each message defined in the interface. The delegate name is composed of the name of the method plus the names of the parameter types. Because it’s a delegate, you can define the method inline. This avoids explicitly writing out the class in full. Stubs are also generated for the getters and setters of properties, for events, and for generic methods. Unfortunately IntelliSense doesn’t support you when you’re typing the name of a delegate, so you will have to open the Fakes assembly in Object Browser in order to check the names. You’ll find that a .fakes file has been added to your project. You can edit it to specify the types for which you want to generate stubs. For more details, see Isolating Unit Test Methods with Microsoft Fakes.

Mocks

A mock is a fake with state. Instead of giving a fixed response to each method call, a mock can vary its responses under the control of the unit tests. It can also log the calls made by the component under test. For example:

C#
[TestClass]

class TestMyComponent
{
[TestMethod]
public void TestVariableContosoPrice()
{
// Arrange:
int priceToReturn;
string companyCodeUsed;
var componentUnderTest = new StockAnalyzer(new StubIStockFeed()
{
GetSharePriceString = (company) =>
{
// Log the parameter value:
companyCodeUsed = company;
// Return the value prescribed by this test:
return priceToReturn;
};
};
priceToReturn = 345;
// Act:
int actualResult = componentUnderTest.GetContosoPrice(priceToReturn);
// Assert:
Assert.AreEqual(priceToReturn, actualResult);
Assert.AreEqual(“CTSO”, companyCodeUsed);
}
…}

Tags: , ,

Leave a Reply

You must be logged in to post a comment.

Партнеры DevOpsHub и DevOpsWiki