Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop

Creating Test Objects With FakeModel

5.00/5 (6 votes)
17 Dec 2013CPOL8 min read 14.4K  
An introduction to using FakeModel to create test aata for you.

Introduction

Organizations don't always have dedicated testers, in fact developers often have to cover their own testing. Creating test data can be a nuisance, especially when dealing with an intricate network of a linear singular purposed large scale system. By this I mean any of the seven systems I work with on a daily basis. We all strive to have the perfect systems created in the correct way, but unfortunately managers exist whose purpose in life is to make developers' lives difficult.

It happens, from time to time, that we need a set of data to run through the testing of a process. FakeModel, like other data creation suites available, allows us to do so in an easy way, with a fluent api that can be likened to LINQ.

Background

There are more mature test data creation suites available, but none that I know of that will recognize DataAnnotations and handle them appropriately, its great for creating data if using an ORM. FakeModel will pay attention to the data annotations attached to a property and react accordingly.

But as I say, it is in it's infancy, currently at Version 0.0.5, as of last night. FakeModel was recommended to me by a University Lecturer when I moaned that it was difficult to find a test data suite that wouldn't ignore my annotations. I don't know how he came about it.

V0.0.4 had a bug with creating properties that were a class, it would create an infinite loop so I had to keep turning this feature off, which I suppose was easy enough;

C#
FakeSetup.AssignClassProperty = false;

This issue has been fixed in V0.0.5, the Release Notes say that Infinite Loops are detected and handled, by ending the creation of new objects, which works well enough.

Using the code

FakeModel can be found on NuGet here. The HomePage can be found on Adam Riddick's website here. Whilst everything is covered, I will give a quick insight into how easy FakeModel is to use.

C#
var myObject = Fake.Begin().Build()

This is the standard FakeModel operation, and will return an object complete with assigned properties.

We can also set specific values of certain properties, of tell FakeModel not to generate a value for them, as follows:

C#
var myObject = Fake.Begin().Assign(x => x.Foo == "Bar").Ignore(x => x.myProp).Build();

Here we are ensuring the Foo property of myObject is set to the value "Bar", whilst the property myProp of the myObject object will be ignored, and thus will hold the default value for its type.

FakeModel also has many other features, such as allowing you to set Constructor Arguments, or creating them itself if you don't and they are required. You can call methods upon creation of the object, and you can assign to Private or Readonly properties.

Unit Testing With Visual Studio Integrated Test Suite and FakeModel

I'll start of by demonstrating the creation of an object using FakeModel to be used in a Visual Studio Integrated Test Suite test scenario, this will be a simple one-model solution at the moment, but I'll go into further detail with more in-depth scenarios after that.

One Model Scenario

In said simple situation, we will test the effectiveness of an extension method for an object, but we don't just want to test a situation that has forced values in case using other values may throw out the code.

We start with a simple Model, named SimpleModel;

C#
public class SimpleModel
{
    [Range(0, 15)]
    public int rangedNumber {get; set;}
    public int number {get; set;}
    public long result {get; set;}
}

Here we have a model that is designed (perhaps poorly) to take in a rangedNumber, between 0 and 15, one which can then hold a result, we can assume safely that the result will be of an unknown calculation of the two numbers.

Within the SimpleModelExtensions class, we have an Extension for SimpleModel which is used to calculate the value of result.

C#
public static long CalculateResult(this SimpleModel model)
{
    model.result = model.rangedNumber + model.numer;
    return model.result;
}

We want to test a large range of numbers to ensure it isn't broken by a certain combination. Instead of generating these numbers manually, we can use a simple loop to ensure we have an uncontrolled, random selection of data - such as the data we may well end up with over a large user base.

To do so, we use FakeModel to create a number of objects within a loop, testing that the outcome is that which is expected.

C#
[TestMethod]
public void TestMethod1()
{
    for(int i = 0; i < 1000; i++)
    {
        SimpleModel obj = Fake<SimpleModel>.Begin().Build();
        obj.CalculateResult();
        Assert.AreEqual(obj.number + obj.rangedNumber, obj.result);
        Assert.IsTrue(obj.rangedNumber <= 15 && obj.rangedNumber >= 0);
    }
}

Change the number from 1000 to a higher number and run the test, you will see in each case (unless I'm yet to hit such a case) that the rangedNumber will always be between the ranged values. That and our extension method does its job nicely.

Albeit being a simple situation, this can show the power of using FakeModel to allow for a number of varying data situations to be tested. In a simple model like this, testing a range could be done without too much hard work by throwing in some random numbers. But when we need a varied number of models with larger numbers of properties, FakeModel really comes into it's own.

Multiple Model Scenario

As easy to use the simple situation was, it's a little unrealistic, you may not encounter an application using a single model in the real world. So we will build on this model to introduce a second and third model and add some dependencies between them.

SimpleModel will be changed so that it holds a ModelB and a ModelC. ModelC in turn will contain a ModelB. Let's discover if FakeModel can handle these.

Our updated SimpleModel:

C#
public class SimpleModel
{
    [Range(0, 15)]
    public int rangedNumber {get; set;}
    public int number {get; set;}
    public long result {get; set;}
    public ModelB b {get; set;}
    public ModelC c {get; set;}
}

ModelB:

C#
public class ModelB
{
    [Range(0, 15)]
    public int number {get; set;}
}

ModelC:

C#
public class ModelC
{
    public ModelB b {get; set;}
}

We'll use another Unit Test to find out if FakeModel can handle these relationships:

C#
[TestMethod]
public void TestMethod2()
{
    for(int i = 0; i < 1000; i++)
    {
        SimleModel obj = Fake<SimpleModel>.Begin().Build();
        ModelC objC = Fake<ModelC>.Begin().Build();

        Assert.IsNotNull(obj.b);
        Assert.IsNotNull(obj.c);
        Assert.IsNotNull(obj.c.b);
        Assert.IsTrue(obj.b.rangedNumber <= 15 && obj.b.rangedNumber >=0);
        Assert.IsTrue(obj.c.b.rangedNumber <= 15 && obj.c.b.rangedNumber >=0);
    }
}

We can see from this simple test that FakeModel handles POCO relationships well, and still respects properties on the next level down. But in this scenario we are playing nice. What if we don't?

The Infinite Loop Scenario

Infinite loops happen, it's a fact. It must be up near the top of the list of the most common software development bugs (I have no facts, I just imagine that this is the case).

FakeModel claims to be able to handle Infinite Loops, as of the latest version (0.0.5) so let's see how well it does.

We will edit ModelC to take a SimpleModel as well as ModelB. SimpleModel has a ModelC property, which again needs a ModelC .... and of course this goes on forever.

C#
public class ModelC
{
    public ModelB b {get; set;}
    public SimpleModel simple {get; set;}
}

Using FakeModel to create either SimpleModel or ModelC will let us test for an infinite loop, if it doesn't end soon then it's broken.

C#
[TestMethod]
public void TestMethod3()
{
    SimpleModel obj = Fake<SimpleModel>.Begin().Build();
    
    //Whats Happening?
    Assert.IsNotNull(obj);
    Assert.IsNotNull(obj.b);
    Assert.IsNotNull(obj.c);
    
    //How far does it go?
    Assert.IsNotNull(obj.c.Simple);
}

Running TestMethod3 will show us the results, obj.c.simple is null, so the tests fails, but removing this line allows the test to pass.

What does that mean?

It means FakeModel has been made intuitive enough to understand that we have an infinite loop in our model. It recognizes the loop, and decides to put an end to it at the early stage.

We have a SimpleModel, we have a ModelC within that SimpleModel, but the second layers SimpleModel does not have it's own ModelC. If we change the line of the test that fails:

C#
Assert.IsNotNull(obj.c.simple);

To check for the ModelC's ModelB:

C#
Assert.IsNotNull(obj.c.b);

We see that the test passes, meaning that SimpleModel's ModelC's ModelB is still created, as it does not run a path that causes an infinite loop. It's pretty intuitive.

Points of Interest

Whilst these demonstrations show what FakeModel can do, they don't highlight it's use in real-world applications. Many such applications become very linear very fast, especially ones specialized to a single process which itself is linear. Should you require 15 or 20 models creating in order to test the final process in such linear systems, then FakeModel will create them for you, and what's better, it will mimic the fact that you don't ever know what your user's will enter as data, pass these tests and you'll be ok.

What I like most about FakeModel is the author's transparency. On the FakeModel page on his personal website, he discusses where he is planning to take FakeModel in the future, and is open to suggestions should people get in touch. I can't say how responsive he is to such suggestions, as I'm yet to try and get in touch. That said, I am going to send in a request for Mocking of Abstract types and Interfaces.

FakeModel currently won't handle collections, but this has been promise in a future release, and hopefully not too distant. We've also been promised a few other features, including more flexible setup options.

Should I Use It?

We have began using FakeModel in two of the applications we maintain. It's not perfect, but then again it's still pre-version 1. FakeModel looks like it will mature like whisky and the author's plans look interesting, but who knows the rate at which these updates will come.

I would suggest using it where you need Data Annotations to be handled, but I wouldn't rely to heavily on it at this stage, most projects end in the early stages - But I really hope this isn't one of them. I would appreciate the author getting FakeModel to a stage where he has completed what he has promised, then I would feel like it's something I could lean against.

For now, I would say take a test drive and use it where needed, and watch this space.

History

  • 09/12/13 - Initial version.
  • 17/12/13 - Added examples.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)