Gustaf Liljegren
Gustaf Liljegren

Reputation: 361

FakeItEasy: Simple Fake not being used

I'm trying to use FakeItEasy (version 4.9.1) to create unit tests for some legacy code. I've created two unit tests, one of which works just as expected, and one very similar which doesn't work despite lots of trial and error.

Here's an excerpt from the method that seems impossible to test:

public class PosMessageProcessor : IPosMessageProcessor
{
    public void Execute()
    {
        PurchaseOrderRepository repo = new PurchaseOrderRepository();
        ...
        try
        {
            m_PurchaseOrder = GetOrderForProcessing(repo);
            ...

And here's my test:

[TestMethod]
[TestCategory("POS")]
public void ExecuteTest()
{
    // Arrange
    PurchaseOrder purchaseOrder = GetPurchaseOrder();
    IPosMessageProcessor fakePosMessageProcessor = A.Fake<IPosMessageProcessor>();
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).Returns(purchaseOrder);

    // Act
    _posMessageProcessor.Execute();

    // Assert
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).MustHaveHappened();
}

The _posMessageProcessor variable is an instance of the PosMessageProcessor class. I want to catch the call to the GetOrderForProcessing() method (within the Execute() method) and make it return my hard-coded purchaseOrder object. But I get another return value (null) instead. Why?

The unit test that works tests the GetOrderForProcessing() method:

[TestMethod]
[TestCategory("POS")]
public void GetOrderForProcessingTest()
{
    // Arrange
    PurchaseOrder purchaseOrder = GetPurchaseOrder();
    IPurchaseOrderRepository fakePurchaseOrderRepository = A.Fake<IPurchaseOrderRepository>();
    A.CallTo(() => fakePurchaseOrderRepository.GetPurchaseOrderByOrderTrackingNumber(A<string>.Ignored)).Returns(purchaseOrder);

    // Act
    PurchaseOrder result = _posMessageProcessor.GetOrderForProcessing(fakePurchaseOrderRepository);

    // Assert
    A.CallTo(() => fakePurchaseOrderRepository.GetPurchaseOrderByOrderTrackingNumber(A<string>.Ignored)).MustHaveHappened();
    Assert.IsNotNull(result);
}

In this case, the call to GetPurchaseOrderByOrderTrackingNumber() returns my hard-coded object as expected. The two tests are virtually the same, except that the GetOrderForProcessing() method takes a parameter while Execute() does not.

Upvotes: 2

Views: 1873

Answers (1)

Blair Conrad
Blair Conrad

Reputation: 241714

ExecuteTest is failing because you configure a fake IPosMessageProcessor and then call Execute on a real PosMessageProcessor, _posMessageProcessor. _posMessageProcessor, being an actual PosMessageProcessor, executes its regular code path, calling the actual Execute, which will call the actual GetOrderForProcessing.

(Note that you can actually delete the fakePosMessageProcessor variable and all references to it from your test, and the behaviour will be the same.)

I do not recommend this kind of testing, but in order to fake GetOrderForProcessing and still test the actual Execute code, you would have to write a test like

public void NewExecuteTest()
{
    // Arrange
    PurchaseOrder purchaseOrder = GetPurchaseOrder();

    // Get a fake PosMessageProcessor that falls back to original behaviour for all calls.
    IPosMessageProcessor fakePosMessageProcessor = A.Fake<PosMessageProcessor>(options => options.CallsBaseMethods());

    // Now fake out the GetOrderForProcessing call so we can test Execute.
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).Returns(purchaseOrder);

    // Act
    fakePosMessageProcessor.Execute();

    // Assert
    A.CallTo(() => fakePosMessageProcessor.GetOrderForProcessing(A<IPurchaseOrderRepository>.Ignored)).MustHaveHappened();
}

Upvotes: 2

Related Questions