Reputation: 361
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
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