FakeModel – Model Faking
Creating test data can be a nuisance, especially in any instance requiring tens or hundreds of test model’s to be created. This is where FakeModel offers to take part of the burden.
FakeModel offers you a Fluent interface for rapid creation of test data, automatically assigning values to properties (public, private and readonly) of an in-built .Net data type or your own POCO classes. Whilst building your model, you can assign the important values yourself and forget about the rest – or even declare that FakeModel should ignore a property in question – all through Lambda Expressions.
FakeModel aims to take the mundane task of Test Data Creation out of your hands, allowing you to focus your brain power* on what really counts.
*Brain Power May Vary.
FakeModel: How To
So let’s get started. First off, install FakeModel through Nuget.
Create An Object
.Begin() must be called to start building the model. Once the setup has been completed, the model can be constructed by calling .Build().
StandardModel model = Fake<StandardModel>.Begin().Build();
Create An Object With Assigned Values
StandardModel model = Fake<StandardModel>.Begin() .Assign(x => x.MyString = "Hello World") .Assign(x => x.MyInt = 789) .Assign(x => x.MyChar = 'M') .Build();
Create An Object Whilst Ignoring A Property
StandardModel model = Fake<StandardModel>.Begin() .Assign(x => x.MyInt = 8) .Ignore(x => x.MyString) .Build();
Create An Object With Constructor Arguments
*Note – If your POCO requires constructor arguments and they are not provided by the HasConstructArgs method, they will be generated by FakeModel and passed in to the constructor.
PublicConstruct model = Fake<PublicConstruct>.Begin() .HasConstructArgs(() => new PublicConstruct("First Parameter", 9)) .Build();
Create An Object Calling Methods On Creation
WithMethods model = Fake<WithMethods>.Begin() .CallMethod(x => x.Addition(2, 3)) .CallMethod(x => x.Multiplication(3, 4)) .Build();
Create An Object Assigning Private or Readonly Properties
This overload can be used to assign Private, Readonly or Public properties.
PrivateSet model = Fake<PrivateSet>.Begin() .Assign(x => x.YouCanSetMe, 8) .Build();
Create Properties Of Class
Properties of poco class types can be setup and assigned as other standard properties can. They can also be ignored, or specific values can be set, in the same manner as any other property can be handled.
FakeModel will detect the creation of an Infinite Loop at prevent it’s creation at the second stage of a Hierarchy. For example, the second occurrence of any class involved within the infinite loop will be the stopping point, after which classes involved in the potential infinite loop will be left as null.
Reverse Properties Of Class Rule
It is possible to reverse the value of FakeSetup.AssignClassProperties on a per-fake basis, using the ReverseClassAssignment() method, available from the fluent API.
PrivateSet model = Fake<PrivateSet>.Begin() .ReverseClassAssignment() .Build();
If, within FakeSetup, AssignClassProperties is set to false, using this method will assign properties of class type for this build, of this type. If the FakeSetup option is set to true, properties of class type will be ignored for this build, of this type.
Create Multiple Objects
FakeModel will handle the creation of collections of objects, offering a fluent API to set values against all of the objects, or subsets of them.
To create a collection, use the following syntax, This will create a collection of 17 objects;
ICollection<T> myCollection = Fake<MyModel> .CreateCollection(17) .Build();
In order to set options for the entire collection, use the following;
ICollection<T> myCollection = Fake<MyModel> .CreateCollection(17) .All() .Assign(x => x.myProperty = 7) .Build();
Once the All() method has been called, after CreateCollection(x), all of the options which are available when creating a single object become available, and are applied to the entire collection.
In order to call those methods on a Subset, use the following syntax;
ICollection<T> myCollection = Fake<MyModel> .CreateCollection(10) .Section(6, 10) .Assign(x => x.myProperty = 92) .Build();
In this case, items starting in the 6th position, and ending in the 10th (.Section(6, 10)) will have the myProperty property set to a value of 92.
All() and Section(start, end) can be used together, such as;
ICollection<T> myCollection = Fake<MyModel> .CreateCollection(10) .All() .Assign(x => x.Name = "Frank") .Section(6, 10) .Assign(x => x.myProperty = 92) .Build();
In this example, every object would have the Name property with a value of “Frank”, and objects 6 to 10 will have a myProperty value of 92.
Multiple sections can also be used in conjunction, and section numbers can overlap. In which case, the final assigned item will be used, for example;
ICollection<T> myCollection = Fake<MyModel> .CreateCollection(10) .Section(6, 10) .Assign(x => x.myProperty = 92) .Section(10, 10) .Assign(x => x.myProperty = 12) .Build();
First, myProperty would be set to 92 for each object in positions 6 to 10. However, 10 would then be overwritten to have a value of 12.
Recognised Data Annotations
FakeModel can recognise and handle the following Data Annotations Attributes;
EmailAddressAttribute – A valid email address will be assigned.
UrlAttribute – A valid Url will be assigned.
MinLengthAttribute – A value of length higher than the MinLengthAttribute value will be assigned.
MaxLengthAttribute – A value of length lower than the MaxLengthAttribute value will be assigned.
StringLengthAttribute – A value of length between the minimum and maximum values will be assigned.
RangeAttribute – A value between the minimum and maximum values will be assigned.
For DateTime, the Range attribute should be used as; [Range(typeof(DateTime), “1/1/2011”, “1/1/2012”]
Should FakeModel experience an issue, it will throw an exception of FakeModelException. The possible exceptions are as follows;
"Expected constructor expression"
Assigned Constructor Expression is not the expected expression type. See “Create An Object With Constructor Arguments” on this page to ensure the constructor arguments are being expressed in the correct way.
Please Note – As of Version 0.0.4 Constructor Arguments will be automatically generated if they are not provided.
"Invalid Ignore Expression"
Assigned expression for ignoring a property is not a MemberAccess Expression Type. See “Create An Object Whilst Ignoring A Property” on this page to ensure ignore statements are passed in the correct way.
"Cannot Instantiate Interface"
The POCO class passed in to FakeModel is an Interface and cannot be instantiated for assignment.
"Cannot Instantiate Abstract Class"
The POCO class passed in to FakeModel is Abstract and cannot be instantiated for assignment.
"Cannot instantiate [type] with the following arguments [Arguments List]"
Incorrect parameters have been provided for the type.
"Cannot Construct Private Constructors"
The instantiation has failed as the POCO only contains a private constructor.
"FakeModel cannot handle type Assembly.MyType"
FakeModel does not support the type in question, If you see this error, please get in touch and support can be discussed.
FakeSetup is present within FakeModel, allowing greater control over the mechanism’s FakeModel uses.
FakeSetup contains the following properties;
Defaulted to true. AssignClassProperty determines if a POCO passed in to FakeModel should automatically have any properties that are of a POCO type generated. If this is set to false such properties will be left with a null value.
A Little Less Conversation A Little More Action Please.
I believe in transparency, as long as they don’t unearth underlying weaknesses anyway. As such, I will keep you all updated as to my future plans for FakeModel within this section. The list currently includes – In no particular order;
- Allowing for Data Sources to be set for use within FakeModel property value generation.
- Creating a set of default data sources with sensible values, such as Names and Addresses.
- Fleunt API access to properties of a class that are themselves a class.
- Allow a limit to be set on infinite loop causing dependencies.
- Fake creation of Interfaces and Abstract types.
- DateTime RangeAttribute Handling.
- I’m hopeful of finding a way of handling CustomAttributes assigned to properties.
Version 0.0.7 -> Added support for properties that are collections, Limited character use within string generation, an option now available within FakeSetup, Range attribute on a DateTime can now be handled in format: [Range(typeof(DateTime), “1/1/2011”, “1/1/2012”]
Version 0.0.6 -> Added fluent API approach to creating collections, Hidden methods not meant to be public (oops!), Added ReverseClassAssignment method, Propertys of a type not supported by FakeModel will cause a FakeModelException.
Version 0.0.5 -> Fixed issues from V0.0.4, Introduced FakeModel Instantiation Hierarchy to prevent Infinite Loops behind the scenes cleanup, changed Private Constructor trace warning to a FakeModelException.
Version 0.0.4 -> Introduced FakeSetup class with AssignClassProperty property, Handled generation of constructor arguments if not provided, for both the top-level POCO and any properties that of POCO types.
Version 0.0.3 -> Added initial support for Data Annotations, Created FakeModelException for Clarity.
Version 0.0.2 -> Added support for properties of POCO Type.
Version 0.0.1 -> Initial Creation