1

I am trying to test the argument passed in a protected virtual method to make sure the properties were set correctly. However the statement below is not setting my variable, but FakeItEasy does recognize that this call is being made. Is my syntax incorrect?

Unit Test

EmailEventArgs argsInEvent = null;

A.CallTo(repository).Where(w => w.Method.Name == "OnSaveRequest")
    .Invokes(i => argsInEvent = i.GetArgument<EmailEventArgs>(0))
    .MustHaveHappened(Repeated.Exactly.Once);

Method in Repository

private void onSaveSetupEmailArgs(string callerName, int pk)
{
    EmailEventArgs args = new EmailEventArgs();

    // ..set property logic

    OnSaveRequest(args); // the protected virtual method
}
jmzagorski
  • 1,135
  • 18
  • 42

1 Answers1

4

It's a little hard to tell what's going on without the whole test. The typical test pattern would be:

  1. create the fake
  2. configure the fake to set up the capturing of the argument
  3. execute the production code, which should use the fake, and
  4. then verify that a call was made.

I can't see where you call the production code, but your A.CallTo seems to be trying both to configure the method and to verify that a call was made. Since you said the MustHaveHappened passes, I'm guessing that this code lives after the call to the production code. Something like:

var repository = A.Fake<IRepository>();

repository.SaveSetup(…);

EmailEventArgs argsInEvent = null;

A.CallTo(repository).Where(w => w.Method.Name == "OnSaveRequest")
    .Invokes(i => argsInEvent = i.GetArgument<EmailEventArgs>(0))
    .MustHaveHappened(Repeated.Exactly.Once);

This will not work (of course you know that, or you wouldn't be asking a question) because you're configuring the Invokes after the production code has been run (and the OnSaveRequest call was already made on the fake).

You should have something like:

// Arrange
EmailEventArgs argsInEvent = null;

var repository = A.Fake<IRepository>();

A.CallTo(repository).Where(w => w.Method.Name == "OnSaveRequest")
    .Invokes(i => argsInEvent = i.GetArgument<EmailEventArgs>(0));

// Act
repository.SaveSetup(…);

// Assert
A.CallTo(repository).Where(w => w.Method.Name == "OnSaveRequest")
    .MustHaveHappened(Repeated.Exactly.Once);

// and maybe do something with argsInEvent
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • i think this will go in my duh! questions. I must have been sitting in front of my computer too long to see that the invoke was after my production code call! So yes that worked. Thanks as always. – jmzagorski Aug 08 '14 at 13:31
  • Cheers. Glad I could help. If it's any consolation, I have been seen a few questions lately that ended up getting variants of this answer. Makes me wonder if there's an opportunity to improve the FakeItEasy docs. To me, doing "arrange -> configure fakes, act -> have production code use fakes, assert -> interrogate the fakes" just makes sense, but it's easy to feel that way when you're "on the inside". Hmm. I could probably save some time by pointing at the [Quickstart](https://github.com/FakeItEasy/FakeItEasy/wiki/Quickstart)… – Blair Conrad Aug 08 '14 at 13:34
  • I did do some searching for an example of the invoke and have read the Quickstart, with numerous other guides. I think the Quickstart tripped me up because it just shows the invoke syntax without me thinking about the production call.(which is all I should you in my example too!) However [this post](http://stackoverflow.com/questions/7133201/getting-arguments-passed-to-a-fakeiteasy-mock-without-using-magic-strings) should have shown me if I paid attention to the flow – jmzagorski Aug 08 '14 at 13:45
  • The AAA test pattern is a great TDD & unit testing pattern - it makes tests a lot cleaner and easier to understand – thinkOfaNumber Jan 24 '19 at 00:54