I have the following test:
[Fact]
public void StartProgram_CallsZoneProgramStart()
{
var zone = A.Fake<Zone>();
zone.StartProgram();
A.CallTo(() => zone.ZoneProgram.Start(null, A.Dummy<ActionBlock<InterruptInfo>>())).MustHaveHappened(Repeated.Exactly.Once);
}
It's creating a dummy of type ActionBlock<InterruptInfo>
which is being passed into the MustHaveHappened call. zone.StartProgram definitely calles the zone.ZoneProgram.Start method, but this call is not seen by FakeItEasy. It returns the following error message:
Assertion failed for the following call:
ZoneLighting.ZoneProgramNS.ZoneProgram.Start(<NULL>, ActionBlock\`1 Id=1)
Expected to find it exactly once but found it #0 times among the calls:
1: ZoneLighting.ZoneProgramNS.ZoneProgram.Start(inputStartingValues: Faked ZoneLighting.ZoneProgramNS.InputStartingValues, interruptQueue: ActionBlock`1 Id=2)
2: ZoneLighting.ZoneProgramNS.ZoneProgram.Start(inputStartingValues: <NULL>, interruptQueue: ActionBlock`1 Id=2)
As can be seen from the error message, the ID on the ActionBlocks being compared are different (1 and 2), which is why it's not able to see that the call is made. My question is, why is the ID of the dummied ActionBlock = 1? I thought being a dummy object, it shouldn't have any concrete details in it like ID etc. Is this because generic types cannot be dummied?
I saw something similar here: https://github.com/FakeItEasy/FakeItEasy/issues/402
But I wasn't able to figure out if that's talking about the same thing or not. Any help would be greatly appreciated.
I am not familiar with
ActionBlock
s, so am not sure where they get theirId
values from, but I think I can shed some light on what's going on in your test.First, I think you're confused about what a
Dummy
is. If so, don't feel bad. They can be a little confusing. From the Dummy documentation, a Dummy isThey are mostly used by FakeItEasy itself when it needs to create an object to feed to class constructors (we'll see more about that later), or when it needs to return a non-fakeable object from a method or property. It's rare for end users to have to create them.
A Dummy is an actual object (it has to be—otherwise, how could we do anything with it?). It has to have whatever concrete details in it that its type has (in this case,
ActionBlock<InterruptInfo>
). There are no restrictions against Dummying generic types.Looking at the docs for how a Dummy is made, we can see that since
ActionBlock<InterruptInfo>
probably doesn't have a customIDummyDefinition
available (do you have one?), and it's not a Task, and it's not fakeable (because the class is sealed), then the Dummy is made by invoking one of the ActionBlock constructors, with Dummies being made to satifsy each of the arguments.I guess that ActionBlocks have IDs. How they're assigned, I have no idea, but if they're a good ID, then it looks like we have two different
ActionBlock<InterruptInfo>
s: one provided inzone.StartProgram
, and the Dummy made in the test.The ActionBlocks documentation suggests that it doesn't override
Equals
, so a reference comparison will be performed, and the two ActionBlocks (the Dummy and the one used in the production code) don't match. This is why FakeItEasy doesn't recognize the call.If you were just trying to see if any call to
zone.ZoneProgram.Start
was made with the first argumentnull
and the second argument some ActionBlock, I think you might've meant to use:(
Ignored
can also be shortened to_
. Read more about ignoring argument values if you're so inclined.)That may get you past your immediate problem, although I have concerns about two things:
zone.ZoneProgram.Start
is being called twice, not "Exactly.Once", but I'm sure you'll be able to deal with this, andI hope that helps a little.
Oh, and you asked about Issue 402. That issue is about giving users more power when defining customization classes that will control how dummies are created. Unless you've made a class extending
IDummyDefinition
orDummyDefinition
, it's probably not relevant at this point.