Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make FakeItEasy capture full argument state?

I have a piece of code like this (simplified):

await realApiClient.DoSomething(entity);
entity.Email = newEmail;
await realApiClient.DoSomethingElse(entity);

In my test I have created a fake and want to check that both DoSomething and DoSomethingElse were called. So I do it like this:

A.CallTo(() => fakeApiClient.DoSomething(A<Entity>
        .That.Matches(p => p.Email == "[email protected]"
    )))
    .MustHaveHappenedOnceExactly();

A.CallTo(() => fakeApiClient.DoSomethingElse(A<Entity>
        .That.Matches(p => p.Email != "[email protected]"
    )))
    .MustHaveHappenedOnceExactly();

The problem - the test for DoSomething fails because FakeItEasy seems to not capture the full state of the method call arguments. When I check the email in DoSomething call, FakeItEasy is checking against the value that was updated in entity.Email = newEmail; and not the original value before the update.

As soon as I comment out entity.Email = newEmail; then DoSomething test succeeds. But then, of course, DoSomethingElse rightfully fails because it expected the email to be changed.

What is the proper way to access the argument state as it actually was at the time when DoSomething was called?

like image 330
JustAMartin Avatar asked Oct 20 '25 16:10

JustAMartin


1 Answers

FakeItEasy doesn't (and likely never will) store a snapshot of an object's state, for reasons discussed mostly in the accepted answer to MustHaveHappened fails when called twice on the same object and also in Verifying multiple method calls with reference parameters.

There are hints there about manually capturing important state to preserve it between calls.

In your simple example, though, one option might be to invoke a custom method in your "arrange" phase, having it capture the important parts of the argument. Something like

var capturedEmails = new List<String>();
A.CallTo(() => fakeApiClient.DoSomething(A<Entity>._))
    .Invokes((Entity e) => capturedEmails.Add(e.Email));
…
await realApiClient.DoSomething(entity);
entity.Email = newEmail;
await realApiClient.DoSomethingElse(entity);
…
capturedEmails.Should().ContainExactly("[email protected]");

(not run nor even compiled but you get the gist)

like image 108
Blair Conrad Avatar answered Oct 23 '25 06:10

Blair Conrad



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!