There are several so-called isolation frameworks, e.g. the ones providing you with the ability to create a fake object and use it inside your unit tests. The most prominent are MoqFakeItEasyRhinoMock and many more. The framework makes no attempt of creating yet another isolation framework, it simpy provides and abstraction sutiable for the most common unit tests cases especially the ones covered by the fake data substitution and builders which will be discussed in further chapters.
As you can see the main functionality to be used by fake-consuming code is setting up the desired behavior via simple methods.
Setup
There are various options for setting up the required behavior. The Attest framework uses fluent API approach where the callbacks should be chained to achieve the desired effect. Let's consider the following interface:
We would like to have an instance of faked object which will produce the required callback upon method invocation. This can be done using the following approach:
Here we set the initial fake providers. The only supported provider is Moq
var initialSetup =ServiceCallFactory.CreateServiceCall(FakeService);var setup =initialSetup.AddMethodCallWithResult<Guid,Guid[]>( t =>t.GetPhasesByGauge(It.IsAny<Guid>()), (r, id) =>r.Complete( k =>/// provide your callback ));
Here we create the initial method call template and then start adding method calls with the required callbacks.
var faked =setup.Build();
Finally we call the Build method to get the faked instance which implements the IPhasesProvider interface.
Builders
The main idea behind using fakes is to simulate some kind of behavior during the test and assert the unit/system state. However this concept of simulated/fake behavior can be easily expanded to the app itself to allow modular and independent development. This means that the faked objects should also become stateful to be able to cope with multiple calls during the app lifetime. Additionally they should still expose the very same interface consumed by the app. To sum it up, the fake data builders combine the storage capabilities with the simulated behavior allowing full app development even without real data providers. Let's see an example:
Here we see an example of state inside the builder which is implemented by _phases field. Additionally the initial setup is implemented by WithPhases method. The two mandatory parts are the implementation of CreateServiceCall method which assigns the actual method calls and the factory method CreateBuilder which is used for dynamic invocations.
This builder is used indirectly during the test by consuming the underlying FakeService and therefore has to remain consistent to ensure proper app simulation.
Data
A typical app consists of several functional layers. In a classic Onion/DDD architecture the central part of every app is the Domain while the Infra layer is an auxiliary one. The Storage functionality is part of the Infra layer as well and should be faimiliar to Domain by abstractions only. This architecture fits perfectly with the fake data providers and fake data provider builders approach. In this case the correspondent module is substituted according to the current configuration.
Let's have a closer look at this code. You can see here that the IAlgorithmProvider is registered into the DI container and the actual implementation is entirely internal. This is extremely useful when we want to switch between the data layers without changing the actual Domain code. The fake data provider itself is merely a redirection to the underlying fake data provider builder. The fake data provider builder is the only stateful implementation of the abstraction's functionality.
You will see in the Testing section how this approach can be used to allow complex data setup and arrange scenarios.