Search code examples
c#fluent-interfacefakeiteasy

C# FakeItEasy and Method Chaining: Only First Call Is Recognized


I have a call to a factory interface that creates an IMachine in a command handler. This returned IMachine from the factory has methods that can be chained together to create itself inside of a builder class. The problem I am running into with FakeItEasy right now is that it only recognized the first call (which is WithSpeeds) unless I configure every method call to return a fake IMachine. Is there a way not to configure every single call or have FakeItEasy recognize every method call on the chain? I know I must be doing something incorrect because if i use OrderAssertions, and fail the order on purpose without setting up my fake machine, it shows calls were made to all the methods. Thanks for the help.

Part of the Command Handler's Method

public void Handle(FooCommand commandParm)
{
  var entity = new Entity.Builder 
  { 
    Machine = _factory.CreateMachine(commandParm.MachineName)
                .WithSpeeds(commandParm.ProcessingSpeed, commandParm.StartupSpeed, commandParm.ShutDownSpeed)
                .WithOils(commandParm.Lubrication, commandParm.FinishingOil)
  };
}

Test

[TestMethod]
public void HandleSetMachineSettings_should_build_machine()
{
  // Arrange
  var settings = CommandUtilities.ReturnFooCommand();
  var _factory = A.Fake<IMachineFactory>();
  var machine = A.Fake<IMachine>();
  A.CallTo(() => _factory.CreateMachine(settings.MachineName)).Returns(machine);

  // Act
  _handler.Handle(settings);

  // Assert
  machine.Should().NotBeNull();
  A.CallTo(machine).Where(x => x.Method.Name.Equals("set_MachineNumber")).WhenArgumentsMatch(arg => arg.Get<int>(0) == settings.MachineNumber).MustHaveHappened(Repeated.Exactly.Once);
  A.CallTo(() => machine.WithSpeeds(commandParm.ProcessingSpeed, commandParm.StartupSpeed, commandParm.ShutDownSpeed)).MustHaveHappened(Repeated.Exactly.Once);
  A.CallTo(() => machine.WithOils(commandParm.Lubrication, commandParm.FinishingOil)).MustHaveHappened(Repeated.Exactly.Once);
}

Solution

  • I take it that WithSpeeds and WithOils both return an IMachine, yes?

    The problem is:

    1. _factory.CreateMachine returns one IMachine, machine from your setup
    2. machine.WithSpeeds returns a fake IMachine that FakeItEasy makes up. This is not machine, but is a different fake, call it "machine2"
    3. machine2.WithOils returns yet another fake machine

    Your Assert block suggests that you expected the same machine to be returned at each step of the builder chain. Try inserting

    A.CallTo(machine)
        .WithReturnType<IMachine>()
        .Returns(machine);
    

    after A.CallTo(() => _factory.CreateMachine()).Returns(machine);

    That way machine will keep returning itself, and the appropriate properties will be set on it and whatnot.