Unit Tests : Testing Business with Fake Data

Jason Heine on May 20, 2009 in C#, Test Driven Development

One of the major issues that I have come across in the past with writing tests for my application is when I get to the point where I need to test data.

There are a couple of things you can do with this.

1. You can create a data base with actual data that you can query by changing your connection string to this temporary database.

2. You can create fake data and use dependency injection to test your business logic.

First, let’s talk about creating a test database. While this is good you are relying on the fact that you actually have to be able to connect to this database. What happens when you are in a continuous integration environment and the server that does all the builds does not have access to the server with the database?

This brings us to the next topic. Fake Data!

So, some of you may be pondering what fake data really is.

What is not fake data?

Fake data is not a database in some SQL server, or Oracle if you prefer.

What fake data is.

Fake data is data that you create either in your objects or in some other static location, such as xml. I would not recommend xml, because you now have to work with opening new larger memory objects just to get your fake data.

So what does this all mean?

Okay, let’s begin with some code. It is always easier to explain a coding concept with code.

First of all, we need a base set of code to work from. I have here a really basic set of classes with methods as shown:

using System;

namespace FakeDataProject
{
    class Program
    {
        static void Main()
        {
            var myBusinessClass = new MyBusinessClass();
            MyObjectForTest result = myBusinessClass.MyMethodThatReturnsString();
            Console.WriteLine(result.Value1);
            Console.ReadLine();
        }
    }

    class MyObjectForTest
    {
        public string Value1 { get; set; }
        public string Value2 { get; set; }
    }

    class MyBusinessClass
    {
        public MyObjectForTest MyMethodThatReturnsString()
        {
            var dataLayerClass = new MyDataLayerClass();
            return dataLayerClass.MyDataLayerMethodThatReturnsString();
        }
    }

    class MyDataLayerClass
    {
        public MyObjectForTest MyDataLayerMethodThatReturnsString()
        {
            //this really calls the database 
            //(and no, this is not the fake data yet)
            //however this will give you an idea of where we are going for testing...
            return new MyObjectForTest
                       {
                           Value1 = "Some value 1",
                           Value2 = "Some value 2"
                       };
        }
    }
}

So, in this code we have the following:

MyObjectForTest – This is our generic object which we will be using to create fake data off of.

MyBusinessClass – This is our core logic.

MyDataLayerClass – Our DAL, where we are going to connect to the database.

 

If you were to copy and paste this code, it will run and you will see a result. Your result should be “Some value 1”

Okay now that all that works. We need to move into the part where we create fake data. If you noticed, we already have fake data in our MyDataLayerClass. While this is true, it is not the ultimate result we want. In reality we will have a database connection.

Now, because we want to use your favorite testing framework for this next step, your code may vary slightly from mine. I will be using NUnit and Resharper.

I have created a new class called Tester.cs

I have placed in my tester class one test with a very basic test.

using NUnit.Framework;

namespace FakeDataProject
{
    [TestFixture]
    public class Tester
    {
        private MyBusinessClass BusinessClass;
        [SetUp]
        public void Setup()
        {
            BusinessClass = new MyBusinessClass();
        }

        [Test]
        public void Length_Of_MyObjectForTest_Variable1_Should_Be_12()
        {
            Assert.That(BusinessClass.MyMethodThatReturnsString().Value1.Length == 12);
        }
    }
}

 

When you run the test, you will get a pass.

Now, we need to get down to the gritty detail on how we are going to produce fake data.

There are a few framework changes that need to happen for this.

First of all, when you write Unit Tests and when you do TDD, you should always be using Interfaces. The reason for this is because you can create fake services which inherit the same interfaces that your real service will inherit.

So, let’s change our main code to use an interface on the service.

interface iMyDataLayerClass
    {
        MyObjectForTest MyDataLayerMethodThatReturnsString();
    }

    class MyDataLayerClass : iMyDataLayerClass
    {
        public MyObjectForTest MyDataLayerMethodThatReturnsString()
        {
            //this really calls the database 
            //(and no, this is not the fake data yet)
            //however this will give you an idea of where we are going for testing...
            return new MyObjectForTest
                       {
                           Value1 = "Some value 1",
                           Value2 = "Some value 2"
                       };
        }
    }

 

As you can see, I took the code from above and just placed an interface on the data layer class.

In our business layer, we need to utilize the interface versus the class.

I went ahead and created a private interface variable and instantiated the class in the method (we will break this out later.)

    class MyBusinessClass
    {
        private iMyDataLayerClass DataLayerClass;
        public MyObjectForTest MyMethodThatReturnsString()
        {
            DataLayerClass = new MyDataLayerClass();
            return DataLayerClass.MyDataLayerMethodThatReturnsString();
        }
    }

 

At this point, if you run your test again, it will still pass. Which means we were able to refactor the core logic and our code still works.

Here is where it gets fun. We are going to use a little thing called Dependency Injection to pass in the data service into the business class. Why are we going to do this? The reason for this is so we can do several things. For one, the business layer no longer is coupled with the data layer. We can now use multiple data layers with the same interface which will help your application be a bit more portable. You can now test your business logic with a fake data service!

Here is the business layer modified to have the interface passed into the business constructor:

class MyBusinessClass
    {

        private readonly iMyDataLayerClass DataLayerClass;

        public MyBusinessClass(iMyDataLayerClass dataLayerClass)
        {
            DataLayerClass = dataLayerClass ?? new MyDataLayerClass();
        }

        public MyObjectForTest MyMethodThatReturnsString()
        {
            return DataLayerClass.MyDataLayerMethodThatReturnsString();
        }
    }

What is happening here?

We have a private variable which is the interface for the data layer.

Our constructor has a parameter to pass in the interface. Inside the constructor, we have a null check for the interface, if it is null we will instantiate a new instance of the class.

Our method now only has 1 line of code, return the result of the data layer.

Granted, our business logic will probably have more to it, but again, this is just a demo.

At this point, if you build, you are going to get an error. The reason for this is your constructor now requires a parameter.

Part of TDD and writing Unit Tests, is your tests will evolve.

Before we update our tests, we need to make our main program work. We need to update our Main() method with the new constructor:

static void Main()
        {
            iMyDataLayerClass dataLayerClass = new MyDataLayerClass();
            var myBusinessClass = new MyBusinessClass(dataLayerClass);
            MyObjectForTest result = myBusinessClass.MyMethodThatReturnsString();
            Console.WriteLine(result.Value1);
            Console.ReadLine();
        }

Now that we have done that, we can update our testing class.

Here is the code with the update:

private MyBusinessClass BusinessClass;
        private iMyDataLayerClass IMyDataLayerClass;

        [SetUp]
        public void Setup()
        {
            IMyDataLayerClass = new MyDataLayerClass();
            BusinessClass = new MyBusinessClass(IMyDataLayerClass);
        }

As you can see, we have a new interface for the data layer and in the setup we are instantiating the class for the data layer.

If you run your test, it should still pass.

Now, we finally get to the point where we can create the fake data.

Inside the tester.cs file I am going to create a new class called FakeDataService. This class will inherit the interface that we created for the real data service:

    class FakeDataService : iMyDataLayerClass
    {
        public MyObjectForTest MyDataLayerMethodThatReturnsString()
        {
            throw new NotImplementedException();
        }
    }

As you can see, we now have all the methods that our interface requires. What we want to do at this point is update our test fixture to use the new fake data service. We update the Setup() method as follows:

        [SetUp]
        public void Setup()
        {
            IMyDataLayerClass = new FakeDataService();
            BusinessClass = new MyBusinessClass(IMyDataLayerClass);
        }

Now, run your test. You will get a failure. If you get a System.NotImplementedException, then you are doing the right thing. You now know that you are  using the new fake data service instead of the real data service.

Now we need to make our test pass. Here is where the fake data comes into play.

You can do this one of a couple of ways.

The best practice is to create an ObjectMother class, which contains all your objects with “fake” data. You can create methods in your ObjectMother such as:

public static MyObject GetValidObject()

public static MyObject GetInvalidObject()

With these methods you can return an instance of that object with valid/invalid data. Each of which you can run tests against. This is important because sometimes your real data layer is going to return invalid data. Doing this will enable you to reuse that valid/invalid data for other tests.

So in this example, I will create a small ObjectMother class which we will use to get our fake data.

class ObjectMother
    {
        public static MyObjectForTest GetValidData()
        {
            return new MyObjectForTest
                       {
                           Value1 = "123456789012",
                           Value2 = "000"
                       };
        }
    }

 

In my fake data service, I will be calling the ObjectMother to get the valid data:

class FakeDataService : iMyDataLayerClass
    {
        public MyObjectForTest MyDataLayerMethodThatReturnsString()
        {
            return ObjectMother.GetValidData();
        }
    }

So, what we are doing here, is we are pretending we are connecting to a database. The object mother is the database, the fake service is the data layer and we are passing in that data layer, using Dependency Injection, into our business layer to handle our logic.

If we run our test after adding this code. It should pass.

One of the ways to know that this is working is to debug your test. When you debug you can see that when you get into your business layer, it is really using the fake data service and not the real one.

Also remember, that the main point of this is to test your business logic based on data that you would receive from the database. You don’t want to test the database to see if that works or not. Your goal should be to test the business layer and your business layer should handle if your data layer does not return valid data.

3 Responses to “Unit Tests : Testing Business with Fake Data”

  1. bonder says:

    Jason,

    Great article. So if you have several cases of invalid data you wanted to test, would you create one method to return a prefilled invalid object per case?

    Example:

    ObjectMother.GetInvalidSSNData();
    ObjectMother.GetInvalidMailingAddressData();
    ObjectMother.GetInvalidOrderTotalData();

    Also, what about sending invalid data into the fake database – do you test that?

    Thanks,

    –Bruce

  2. Jason Heine says:

    Hi Bruce,

    Yes, this would be correct. Your ObjectMother would have specific scenarios of handling different invalid data. Also, you can pass in data to your fake database. Typically your logic will be handled in the business layer for validation, but if something does not get validated properly, you could put some logic in your fake service layer that would replicate what would happen if you were to do this in a real database.

  3. Sergio says:

    Cool Article,

    In my expiriance maybe some mocking tool will help you mock your business objects withouth need to implement any interface or do code your fake data classes.
    For example you may use MOQ framework in .NET projects.

    However, really cool article. Keep up the good work.

Leave a Reply