Click here to Skip to main content
Click here to Skip to main content
Go to top

Data Aware Unit Testing

, 10 Oct 2006
Rate this:
Please Sign up or sign in to vote.
An article discussing strategies for unit testing in data aware environments.

Introduction

Unit tests are all the rage these days and I am a big fan; however, I think there is a danger on over reliance on unit tests and more importantly writing unit tests which have little or no value. I've seen one major project and heard of several others fail due to over zealous consultants writing reams of unit tests which have little real value. At the end of the lifecycle the client is left with a semi functioning application and an impressive collection of green lights in the testing tool.

One thing I commonly see done in a less than optimal manner is database unit testing; this to me is not unit testing, it is integration testing. Don't get me wrong, integration testing is an essential process during a project life cycle, but unit tests should be completely encapsulated with no unnecessary dependencies and no footprint, i.e., no lasting alteration to a data source, be it a database, file, or any other storage medium.

When I deploy my unit tests on my testing server, I don't want to have to permit that server for access to a database and all the associated headaches involved. I also don’t want tests manipulating data that may in turn effect the results of other tests – that’s for the integration tests.

I've often been curious as to how to perform meaningful unit tests on code which has some sort of data dependency, and let's face it, 90% of code we write will probably need to communicate with a database at some point during its life cycle. Here I present my thoughts, I hope this will trigger discussion as to the best way to tackle data testing or at the very least provide you with some food for thought.

Mock your world

Mocking objects in unit tests is a useful technique to test the core logic of the class whilst removing any process that may have a heavy or unnecessary overhead such as performing a complex join against a database. There are many third party tools to help you mock objects but personally I find them buggy and unintuitive therefore I do my own mocking which is remarkably straightforward.

The following is an example of a data centric unit testing approach.

public abstract class GradingProcess
{
    protected int _id;
    protected string _connectionProperties = null;

    public void Process()
    {
        this._connectionProperties = GetConnectionString();
        DataSet rawGrades = GetData();
        this.CalculateGrades(rawGrades);
    }

    public void CalculateGrades(DataSet rawGrades)
    {
        //...iterate through and do
        //   something useful with the data 
    }

    protected abstract string GetConnectionString();

    protected DataSet GetData()
    {
        SqlCommand comm = 
          new SqlCommand("select * from grades where id=@id");
        SqlConnection conn = 
          new SqlConnection(this._connectionProperties);
        comm.Connection = conn;
        comm.Parameters.Add(new SqlParameter("@id", this._id));
        SqlDataAdapter sda = new SqlDataAdapter(comm);
        DataSet ds = new DataSet();

        try
        {
            conn.Open();
            sda.Fill(ds);
        } 
        finally
        {
            conn.Close();
        }

        return ds;
    }
}


   
    public class Student : GradingProcess
    {
        protected override string GetConnectionString()
        {
            return "production server settings";
        }
    }

    public class MockStudent : GradingProcess
    {
        protected override string GetConnectionString()
        {
            return "dev server settings";
        }
    }

[Test]
public void CanWeGrade()
{
    GradingProcess mock = new MockStudent();

    mock.Process();
}

The above example is hypothetical but is used to demonstrate an approach I’ve seen used in unit tests. The student class is the concrete class that would be used in our application, the MockStudent class is used solely within our unit test environment. Notice that the mock classes conform to an abstract class that controls the process flow in the sub classes.

For the purpose of this article, the classes are lumped together. In the real world I would expect the abstract classes to be in an assembly referenced by the unit tests and the application we are writing. This test doesn’t achieve much other than calling the process method but I want to focus on the structure of the code. If you were doing this for real you’d test properties of the mock class to ensure results were correct.

What I don’t like about this is it simply swaps one dependency for another, in this case the development database for the production database. The above test is what I’d refer to as an integration test and as such has value in the integration testing phase but is not really suited to unit testing.

Enter the DrAgOn

The data access object (DAO) is a neat way of encapsulating data access to help us on our unit testing mission. Its responsible for all interaction with the data source, the calling class doesn’t care where the data is coming from it simply talks to the DAO.

Please take a look at the following code:

public interface IDAO
{
    DataSet GetData(int id);
} 

public class MockDAO : IDAO
{
    #region IDAO Members
    public DataSet GetData(int id)
    {
        DataSet ds = new DataSet(); 
        ds.Tables.Add(new DataTable("grades"));
        ds.Tables[0].Columns.Add(new DataColumn("Name"));
        ds.Tables[0].Columns.Add(new DataColumn("Result"));
        DataRow row = ds.Tables[0].NewRow();
        row["name"] = "John Jones";
        row["result"] = "90%";
        ds.Tables[0].Rows.Add(row);
        return ds;
    }
    #endregion
}

    public class ConcreteDAO : IDAO
    {
        private string _connectionProperties = 
                        "prod server settings";

        public DataSet GetData(int id)
        {
            SqlCommand comm = 
              new SqlCommand("select * from grades where id=@id");
            SqlConnection conn = 
              new SqlConnection(this._connectionProperties);
            comm.Connection = conn;
            comm.Parameters.Add(new SqlParameter("@id", id));
            SqlDataAdapter sda = new SqlDataAdapter(comm);
            DataSet ds = new DataSet();
            sda.Fill(ds);
            SqlDataReader studentGrades = null;

            try
            {
                conn.Open();
                studentGrades = comm.ExecuteReader();
            } 
            finally
            {
                conn.Close();
            }

            return ds;
        }
    }


    public class Student 
    {
        IDAO _dataLayer;
        private int _id;

        public Student(IDAO dao)
        {
            this._dataLayer = dao;
        }

        public void Process()
        {
            DataSet rawGrades = 
              this._dataLayer.GetData(this._id);
            this.CalculateGrades(rawGrades);
        }

        private void CalculateGrades(DataSet rawGrades)
        {
            ...do something useful
        }
    }
    [Test]
    public void CanWeGrade()
    {
        IDAO mock = new MockDAO();
        Student student = new Student(mock);
        student.Process();
    }

If you compare the two code snippets you’ll notice that we no longer require a mock student class rather we mock the DAO. The DAO has to conform to an interface (IDAO) which its concrete equivalent also has to conform to. In order to test we instantiate a concrete student class and pass in the mock and call the process method. The beauty of this approach is we have complete control over the mock data and can use it to test a variety of scenarios.

For simplicity, I’ve again combined all the classes together.

Conclusion

Sensible unit testing is a valuable tool in any developers arsenal. Research suggests that used properly it can reduce bugs in production by up to 30%. Database dependent class testing is something I have seen done badly or completely skipped, I hope this article provides some help and If the response is favourable I may develop the idea further.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

cgreen69
Software Developer (Senior)
United Kingdom United Kingdom
A .net devotee specialising in Object Orientated web development for financial institutions in Europe. When not working can normally be found at a bar within walking distance of the office.

Comments and Discussions

 
GeneralA nice tool that can help on the matter Pinmemberegozi1317-Oct-06 12:28 
GeneralAdd in a DAO Factory PinmemberAkaitatsu17-Oct-06 4:19 
GeneralRe: Add in a DAO Factory Pinmembercgreen6917-Oct-06 4:47 
GeneralA good start Pinmembercraigg7517-Oct-06 4:17 
GeneralRe: A good start Pinmembercgreen6917-Oct-06 4:56 
GeneralRe: A good start PinmemberAkaitatsu17-Oct-06 6:10 
GeneralMocks aren't stubs PinmemberTimMerksem16-Oct-06 22:24 
GeneralRe: Mocks aren't stubs Pinmembercgreen6916-Oct-06 22:36 
GeneralRe: Mocks aren't stubs PinmemberTimMerksem16-Oct-06 23:06 
GeneralRe: Mocks aren't stubs Pinmembercgreen6917-Oct-06 1:23 
GeneralRe: Mocks aren't stubs PinmemberTimMerksem17-Oct-06 3:24 
A quote,
 
As I said at the beginning, mocks are often confused with stubs. This is quite easy to understand since the various tools you can obtain to create mocks are perfect for writing stubs. The important difference is not what they are but how they are used.
Stubs are typically used to stub out objects that are expensive to create or manipulate. A classic example might be a database connection. As a result you tend to most often find stubs at the edges of a system, or around complex clusters of objects within a system. To create a stub you form an alternative implementation of an interface and replace the real methods with simple canned data.
 
The key difference with mocks is the expectation setting mechanism where you test which methods were called on the mock. Mockists often refer to a mock object that just returns values as 'just a stub'. So one way of looking at the difference between mocks and stubs is that with mocks you set and test expectations as part of your test. As it turns out, that's a little simplistic - I've certainly written stubs that do some simple form of expectation checking such as setting a boolean field if a certain method is called. However I think it's reasonable to say that expectations are a rare feature for stubs but the primary feature for mocks.
 
The biggest issue isn't really the difference between mocks and stubs. It's the interaction vs state style that's most interesting. Interaction-based testers write mocks for all secondary objects. State-based testers write stubs only for those objects where it's not practical to use the real object - external services, things that are expensive, and some things that are awkward to test in a state-based way such as a cache.
GeneralRe: Mocks aren't stubs Pinmembercgreen6917-Oct-06 4:39 
QuestionAm I missing something? PinmemberBil Irving10-Oct-06 8:41 
AnswerRe: Am I missing something? Pinmembercgreen6910-Oct-06 9:23 
GeneralRe: Am I missing something? PinmemberBil Irving10-Oct-06 20:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 10 Oct 2006
Article Copyright 2006 by cgreen69
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid