Building Class Composers
Under Construction
This page is under construction and may contain incomplete or incorrect information.
Looking over in the demo project, this example can be found here: FakeTests
public interface IFakeService
{
int GetOne();
}
[Fakes]
public class FakeTests(Fake<IFakeService> fake)
{
public Fact FakeIsReturnsTheValue()
{
fake.CallsTo(x => x.GetOne())
.Returns(1);
return fake.FakedObject.GetOne() == 1;
}
}
What actually happens behind the scence is the implementation of two types:
- A
ZeComposerAttribute
generic implementation that points to an instance of aIZeClassComposer
class. - The
IZeClassComposer
implementation defined by theZeComposerAttribute
which knows how to take data from theZeComposerAttribute
and create the types the class is dependent on.
A natural implementation includes the LamarContainerAttribute
or the FakesAttribute
this article digs into.
Building the Composer
For ZeUnit to know how to populate the dependencies of a test class it will look to a collection of IZeClassComposer
instances, asking each of the composers for the classes it needs and doing its best to compose from all the response. Here is the FakesClassComposer
public class FakesClassComposer : IZeClassComposer
{
protected Dictionary<Type, Func<object>> factory = new Dictionary<Type, Func<object>>();
public object? Get(Type args)
{
var genericType = args.GetGenericArguments().FirstOrDefault();
if (genericType == null || !args.FullName.StartsWith("FakeItEasy.Fake"))
{
return default;
}
if (!factory.ContainsKey(genericType))
{
Type fakeType = typeof(Fake<>).MakeGenericType(new Type[] { genericType });
factory.Add(genericType, () => Activator.CreateInstance(fakeType)!);
}
return factory[genericType]();
}
}
Part of the big function here is that the IZeClassComposer
interface returns the Nullable<object>
from the get call, allowing a composer to fail to return a type.
In this case, we are not really interested in any information as this IZeClassComposer
is generic looking at the Type args
to both be a generic argument and to be of a type FakeItEasy.Fake
. And knowing that if we request that type we should make the generic version of new Fake<TType>()
something that we do caching the reflection call to the activator into a function.
The Composer Attribute
And the easy part, just creating the marker attribute that lets ZeUnit know that the FakesClassComposer
is used by the test class. This can be thought of as the arguments for the constructor, in the case of our FakesClassComposer
it has no arguments but other classes might.
public class FakesAttribute : ZeComposerAttribute<FakesClassComposer>
{
}
A generic and none-reflection version of the same type of class also exists in the form of FakeAttribute<TType>
which informs composer composer class also keyed to the TType
generic to only create the Fake<TType>
when the args request the specific TType
type.
public class FakeAttribute<TType>
: ZeComposerAttribute<FakeClassComposer<TType>>
where TType : class
{
}
Breaking the Test Mold
The traditional thinking about tests has some purity standards that in the case of this type of work is really breaking the mold. Test dependencies are the responsiblity of // Assemble
but ZeUnit has done away with the traditional view of //Assert
and these composer methods are a re-usable method of sharing the //Assemble
part of your class.
Do you have a repeating pattern of how you build your fakes? Get it directly inject with a IZeClassComposer
class and a quick ZeComposerAttribute
marker class and all your tests objects and fakes they need.
Next Section Customizing Reporting