Awesome
SelfInitializingFakes
A framework for creating self-initializing fakes. To quote Martin Fowler,
The first time you call the fake it passes the call onto the actual remote service, and as it returns the data it takes and saves a copy. Further calls just return the copy.
The self-initializing fakes are not intended to be used as general-purpose, configurable dynamic fakes. Instead, once they are created, and have recorded a sequence of calls against a real service, they expect to encounter those same calls again, and will return the same results.
This package was created to provide the same functionality as the self-initializing fakes in FakeItEasy, which were recently deprecated from the .NET 4.0 version of the library and do not exist at all in the .NET Standard version.
Highlights
- only requires a "real" service to exist when recording
- built-in binary (.NET Framework only) and XML serializers
- user-supplied serializers can provide flexible storage
Usage
Assuming a class Service
that implements IService
, a self-initializing fake version could be used like so:
var callRepository = new XmlFileRecordedCallRepository("calls.xml");
using (var selfInitializingService = SelfInitializingFake<IService>.For(() => new Service(), callRepository))
{
var systemUnderTest = new SystemUnderTest(selfInitializingService.Object);
systemUnderTest.DoSomething(); // internally uses selfInitializingService.Object
}
Let's examine this line by line. The first time this code is run, it:
- Creates a new call repository that will save recorded calls in the file "calls.xml".
- Creates a new self-initializing fake. Because the call repository has never been filled, this self-initializing fake wraps the
new Service()
object. - This is just a line with a brace. It doesn't do much.
- Obtains a fake object (of type
IService
) from the self-initializing fake and injects it into a new system under test object. - Exercises the system under test, which will use the
selfInitializingService.Object
as a collaborator. All calls made toselfInitializingService.Object
will be forwarded to the concreteService
object. - Disposes of the
selfInitializingService
, which will cause it to save all calls made to it (and responses) to thecallRepository
.
So far, the self-initializing fake has provided no benefit; a real Service
object was used, and did all the work it would normally do.
In order to realize a benefit, "calls.xml" should be retained (ideally placed under version control) for subsequent test runs.
Then when the test is run again, the code:
- Creates a new call repository that will load recorded calls from the file "calls.xml".
- Creates a new self-initializing fake. Because the call repository was previously filled with calls, this self-initializing fake does not need to create a
new Service()
. - This is just a line with a brace. It doesn't do much.
- Obtains a fake object (of type
IService
) from the self-initializing fake and injects it into a new system under test object. - Exercises the system under test, which will use the
selfInitializingService.Object
as a collaborator. All calls made toselfInitializingService.Object
will be handled by the object-faking mechanism. - Disposes of the
selfInitializingService
, which does nothing, because there's no need to save the calls to "calls.xml".
If at some point the interactions with Service
need to change, or a real-life Service
is found to behave differently
than it used to, the old call repository ("calls.xml" in this example) can be removed. Then a new test run can
regenerate it and it's good to use thereafter.
Logo: Self Obsession by Michael A. Salter from the Noun Project.