Click here to Skip to main content
Click here to Skip to main content

Unit testing enumerations which map to a database table

, 26 Sep 2006
Rate this:
Please Sign up or sign in to vote.
How to ensure an enumeration is up-to-date with a table in the database.

Introduction

This article explains how to unit test an enumeration which maps a table in the database. The concept is simple: how to ensure an enumeration is up-to-date with a table in the database? And why would you want to do that?

Background

Almost every project has some sort of enumeration that represents the values in a database table. It might be some sort of order status, a category type etc. If an enumeration is present in both code and the database, this makes room for inconsistency. You might update the database and forget to update the code, or do the opposite, and this can introduce problems. It's all about reducing human errors, and making code consistent and easy to maintain. The earlier you are aware of an issue, the better.

Another option is to look into code generation, but that's another article. You can also use this unit test in addition to code generation to make it bullet proof.

How does it work?

I will not delve into the code behind the validation, but I will explain how it works and how you can make use of this feature. Please download the source code if you are interested about what happens behind the scenes.

A custom attribute is put on the enumeration that is to be validated. This attribute describes which table the enumeration corresponds to. A unit test runs the validation, and all enumerations are fetched by using reflection. An enumeration is mapped to a list of fields of type EnumField, which contains its name and value. E.g. "Winter" and "1". This is also done with the table in the database, and then the two lists are compared with each other and any differences will be discovered. See figure below.

Using the code

In order to validate an enum, we can't really hardcode it. The trick is to use an attribute and tag the enumerations.

using EnumValidation;

[ValidateEnum()]
public enum Season
{
    Winter = 1,
    Spring,
    Summer,
    Autumn
}

If the table name differs from the enum name, simply supply the table name in the attribute constructor.

[ValidateEnum("SeasonType")]
public enum Season
...

Fort mapping a bitwise enumeration, All needs to be present in the database in order to make it work.

[Flags]
[ValidateEnum()]
public enum Season
{
    Winter = 1,
    Spring,
    Summer,
    Autumn,
    All = Winter | Spring | Summer | Autumn
    //should be 7 in database
}

In some cases, you can't use the name column in the table. It might be used for a description text for a dropdown list etc. In such a case, you should add an extra column that represents the enum field name, e.g., a new column named "EnumFieldName". To map this in the code, add the column name after the table name, like this:

[ValidateEnum("Season", "EnumFieldName")]
...

So far, so good. This explains how to set an attribute and include the enumeration in the validation process. Now you need a unit test to trigger the validation. The unit test below will start the validation and hook up on the Validated event. Any validation is reported through this event, whether it succeeded or not. The Validate method will only return true if all the validations succeeded. To run a validation you need to supply a connection to the database. You can either pass a SQL connection or a connection string in the constructor to use the built-in connection provider.

[TestMethod]
public void ValidateEnums()
{
    EnumValidator validator = new EnumValidator(ConnectionString);
    validator.Validated += new 
      EventHandler<EnumValidationEventArgs>(validator_Validated);

    Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();

    bool success = validator.Validate(assemblies);

    Assert.IsTrue(success, "One or more validations failed," + 
                  " or an error occured. Please look " + 
                  "at the error log for details.");
}

As you see, we hook up the Validated event. If a validation fails, you will get an error description of which name or value went wrong. If an exception occurs, maybe because the wrong table name was submitted, an error message accompanied with an exception will give the details about this. I recommend you use some sort of log tool to be noticed about any issues.

void validator_Validated(object sender, EnumValidationEventArgs e)
{
    string message = e.Message;
    //p.s. you can get the attribute that was validated 
    //through the e.Attribute property.

    if (!e.Success)
    {
        Console.WriteLine(message);

        if (e.HasException)
        {
            //This will occur if the validation threw an exception. 
            //E.g. the table name defined in the attribute was wrong.
            //e.g. logX.LogError(message)
        }
        else
        {
            //This will occur if the validation failed because 
            //of mismatch between the database and the code.
            //e.g. logX.LogWarning(message)
        }
    }
}

Let's have a look on some of the properties that can be set on the validation object.

Use the Target property if you want to control the direction of the validation. In the example below, I have set that the code should be validated up against the database. The default choice is Target.Both which will run a cross validation, and all errors will be detected.

validator.Target = Target.Code;

Another option is the IgnoreCase property. This one is, by default, set to false. If this one is set to true, comparison of the enum field name will be case insensitive.

validator.IgnoreCase = true;

Getting started

  • Download the demo project if you want to play around and test the validation. I have included a test case for both VSTS (Visual Studio Team System) and NUnit. Use the one appropriate. There is also a Season.sql included. Run this in order to create the test table, and fill it with test data. Remember to edit the connection string in the unit test.
  • Download the source if you want to modify or compile the validation code. No unit tests included.

To start using this in your own project, copy and reference the DLL in your project. Make a new unit test, or copy the one supplied in the demo project. Attach some sort of log tool to be notified of errors. You might need to reconsider how to fetch the different DLLs. AppDomain.CurrentDomain.GetAssemblies() will only get the DLLs referenced and used in your test project.

Well, that's it. Download the demo/source. Try it. Give some feedback.

History

  • September 12, 2006 - Created article.

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

Oyvind Hansen
Web Developer
Norway Norway
Øyvind Hansen works as a Microsoft/.NET consultant for Objectware in Oslo, Norway.

Comments and Discussions

 
GeneralNice but not really (what I understand) as a test method PinmemberMLansdaal21-May-07 11:07 
GeneralNice idea Pinmemberrashack5-Nov-06 11:44 

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 | Terms of Use | Mobile
Web01 | 2.8.1411023.1 | Last Updated 26 Sep 2006
Article Copyright 2006 by Oyvind Hansen
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid