How to fake an action<> with FakeItEasy

2019-06-25 23:46发布

问题:

I'm working with the FakeItEasy library to create fakes for my unit tests.

I have a ClassUnderTest on which I want to test the method MethodToTest(Data dataObject). This method is calling a method of an interface which I want to fake:

public interface IFoo
{
  void Execute(Action<IDataAccess> action);
}

public class ClassUnderTest
{
  private IFoo _foo;

  public ClassUnderTest(IFoo foo)
  {
    _foo = foo;
  }

  public void MethodToTest(Data dataObject)
  {
    _foo.Execute(dataAccess => dataAccess.Update(dataObject));
  }
}

public interface IDataAccess
{
  void Update(Data data);
}

public class Data
{
  public int Property { get; set; }
}

In my unit tests I want to check if the test method calls the interface correctly (with the correct property value):

[TestClass]
public class UnitTest1
{
  [TestMethod]
  public void TestMethod1()
  {
    var foo = A.Fake<IFoo>(x => x.Strict());
    A.CallTo(() => foo.Execute(dataAccess => dataAccess.Update(A<Data>.That.Matches(d => d.Property == 20))));      

    var cut = new ClassUnderTest(foo);

    cut.MethodToTest(new Data { Property = 20 });      
  }
}

But something is configured wrong in this test. I get the exception:

Test method TestProject1.UnitTest1.TestMethod1 threw exception: FakeItEasy.ExpectationException: Call to non configured method "Execute" of strict fake.

Does somebody have an idea of how I have to configure the CallTo() statement correctly?

Thanks in advance!

回答1:

The updated example really helps, @rhe1980.

First some notes about the test you supplied:

  1. the A.CallTo method doesn't do anything - it's not setting up behaviour (with a .Invokes or a .Returns or even a .DoesNothing) or verifying that the method has been called (for example with .MustHaveHappened).
  2. Comparing Actions appears to be tough. I did find some advice over at Compare Delegates Action<T>, but if it were me, I'd take a slightly different tack.

Instead of attempting to compare the Action delegate to a reference model, I figured I could emulate this by capturing the action supplied to Execute and then running it on an IDataAccess and see what the action does. Fortunately, we have FakeItEasy to help with that!

I had success with this test:

[TestMethod]
public void TestMethod1()
{
    // Arrange
    var foo = A.Fake<IFoo>(x => x.Strict());

    var fakeDataAccess = A.Fake<IDataAccess>();

    A.CallTo(() => foo.Execute(A<Action<IDataAccess>>.Ignored))
                    .Invokes((Action<IDataAccess> action)=>action(fakeDataAccess));

    var cut = new ClassUnderTest(foo);

    // Act
    cut.MethodToTest(new Data { Property = 20 });

    // Assert
    A.CallTo(() => fakeDataAccess.Update(A<Data>.That.Matches(d => d.Property == 20)))
        .MustHaveHappened();
}

I hope it helps.



回答2:

If I understood your intentions correctly, you need to use something as follows:

A.CallTo(() => foo.Execute(A<Action<IDataAccess>>.Ignored).MustHaveHappened();