TDD validation... NetTiers Style

Let’s face it. At the end of the day, even the most purist of us object-oriented bigots know the real truth. Objects need data to work. Yes, behaviour defines an object and all that OOD popycock but I mean, a Customer domain object still needs a name, telephone number, and address in order to be of any value in an Enterprise system right? Sure there are “business rules” we want to write but all the unit tests in the world are not going to get around the fact that objects need data and sometimes that data needs to be saved (or retrieved, or both).

The problem we all face, sooner or later, is how do I get data from some datasource (for the sake of the argument here, let’s say a database) into (and out of) my object? There are really two ways to go about it and for the purpose of this article, let’s avoid the O/R mapping ramble. So that leaves us plucking data from properties exposed in a business object and shuffling it off to parts unknown to be stored somewhere. Service objects and Repositories and Mappers and Translators and all kinds of other things having intimate knowledge of the types and properties of domain objects. On the flipside of this, there’s a pattern called ActiveRecord that follows the principle that an object would communicate with an outside service and publish it’s properties for persistence (or the other way round loading data from a data source). Neither of these situations is really pleasant but it’s a bear we deal with when we have to shut down machines and expect that information to be there tommorow.

The latest version of a set of CodeSmith templates called NetTiers provides us with an interesting way to look at our domain (and getting it to/from a database). CodeSmith is a code generation tool and using the NetTiers templates, it creates a n-tier set of classes (about 90 of them from a simple database) that allow you to do cool stuff like this:

DataRepository.MyObjectProvider.Save(MyObject);

This is all neatly wrapped up in a transaction (if the database supports it and/or you configure it to use transactions) and all dynamically generated based on tables and columns in a database. It’s all very slick and NetTiers can create an entire app (minus the front-end) for you in less than 10 seconds. So what’s this got to do with TDD?

Imagine we’re starting an application and we’re going to be building it using TDD. We have some requirements and we’re ready to start writing tests. Given a requirement that a Customer name can not exceed 50 characters we might write a test like this:

namespace NetTiersTDD.UnitTests

{

    [TestFixture]

    public class CustomerFixture

    {

        [Test]

        [ExpectedException(typeof(ApplicationException))]

        public void CustomerNameShouldNotExceed50Characters()

        {

            Customer customer = new Customer();

            customer.Name = "X".PadLeft(51);

        }

    }

}

This is fine and a good start. So now we create our business entity to support the test:

namespace NetTiersTDD.Entities

{

    public class Customer

    {

        private string _name;

        public string Name

        {

            set { _name = value; }

        }

    }

}

Great. Our unit test compiles but out test fails (which is good, remember Red-Green-Refactor). Now let’s make the test pass in our business entity:

namespace NetTiersTDD.Entities

{

    public class Customer

    {

        private string _name;

        public string Name

        {

            set

            {

                if(value.Length > 50)

                    throw new ApplicationException();

                _name = value;

            }

        }

    }

}

Fantastic. So this can be typical when doing something like these checks. There are other checks you might do and the general idea is around failing fast so you would throw an exception when some validation fails on your business entity (you don’t want to find out your domain object is invalid several layers or operations down the stack).

Fast forward about 3 days and you find yourself slugging through requirements like this (and others) and your classes start buffing up and out. More domain logic, lots of tests, goodness. However, you might notice something happening with some of your tests. Take a look at our Customer test now:

namespace NetTiersTDD.UnitTests

{

    [TestFixture]

    public class CustomerFixture

    {

        [Test]

        [ExpectedException(typeof(ApplicationException))]

        public void CustomerNameShouldNotExceed50Characters()

        {

            Customer customer = new Customer();

            customer.Name = "X".PadLeft(51);

        }

 

        [Test]

        [ExpectedException(typeof(ApplicationException))]

        public void CustomerAddressShouldNotExceed255Characters()

        {

            Customer customer = new Customer();

            customer.Address = "X".PadLeft(256);

        }

 

        [Test]

        [ExpectedException(typeof(ApplicationException))]

        public void CustomerAgeShouldNotExceed65()

        {

            Customer customer = new Customer();

            customer.Age = 66;

        }

    }

}

(note: I’m using ApplicationException here but you would probably have a specific exception for different validations)

Lots of exceptions being thrown because well, it’s a business rule violation. By design, we don’t want to make our domain object invalid by any means so we throw an exception when setting a property that’s invalid. This will immediately let us know something is wrong and, if we had a UI, we could inform the user of the error (maybe with a Message Box telling him that a field is too long or required or whatever). This is fine and dandy but makes me queasy. I mean, exceptions are expensive. Imagine I had an import routine that brought in crappy (un-validated) data from an Excel spreadsheet and created 1,000 domain objects in a collection. That’s a heck of a lot of exceptions.

An alternative is to post-validate an object. That’s a valid approach. Let whatever information come into the system (from a user or that crappy spreadsheet) and validate the object after the fact. Then I can do something like check an IsValid property (or the return value from a method called Validate()) and act accordingly. I’m still not letting my domain object get out of control because the unit of work hasn’t completed until I validate the object so we’re good to go. This is okay but now we need a Validate method, some way to retrieve the validity of our business object, some way to set the rules for various properties, and other “helper” methods. Some astute readers will jump up and say “I know, let’s decorate properties with attributes and use reflection in a standard method to validate a setter”. Cool and nice out-of-the-box thinking and certainly something you could do (and something that might start to drift into AOP territory but that’s a whole ‘nuther can o’ worms that I won’t get into either).

So other than reflection, I now have to write a bevy of code to handle validation, invalid state, modify changes to properties (or maybe respond to them) and lots of other little niggly bits. If I was being paid by the line then maybe I want to do this. And hey, if I build a framework we can use it everywhere in the organization so it’s an investment. Yes, this is again a valid thing to do but my motto is don’t write something that can be created by a tool for you (and works). Using the validation framework generated from NetTiers, we can get all this (and more) for a pretty insignificant cost. On top of that, we have a DAL written for us that we don’t have to worry about and can use later when we need to persist our business entities (and what application doesn’t do that?).

Again Bil, what the heck does this have to do with TDD?

Like I said, NetTiers (via CodeSmith) will generate billions of lines of code for you with the single click of a button. Buried in that code are some base classes that NetTiers will use with your business entities (gen’d from your tables based on it’s columns and constraints) and buried deeper down than that are some pretty cool utilities and classes that you can use to write less code (but get more accomplished). I’m all for something that works and let’s me write less code to get more done and that’s what I can get from NetTiers.

Let’s go back to our Customer class and try some NetTeirsTDD. First thing is you need NetTiers to generate you it’s artifacts. This involves creating a database and a table. Yeah, I know. TDD. Database. The TDD guys are saying “Oh man, he’s got it all wrong”. Just walk with me on this okay?

Create a new database and add a simple table to it. Here’s the one I used:

nettiers_table

The table is called Ernie with ID and Title columns. The name and columns really don’t matter so you can call it Temp, Balloon, Mayonaise, or Wurstie for what it’s worth. Just call it something that won’t be one of your business objects because hey, we’re doing TDD and we don’t create business objects until we need them right? The temp table (and resulting generated code) is so you have a starting point (and a framework to leverage, which is what we’re getting to).

Now run CodeSmith using the NetTiers templates. In the latest version (2.0) of the template, you’ll need to set a few things:

  • Point it at your datasource via the ChooseSourceDatabase property
  • Set IncludeUnitTest to True (this will create a unit test project)
  • Set LaunchVisualStudio to True
  • Set GenerateWebLibrary and GenerateWebSite to False
  • Set ViewReport to False (or leave it on if you want, your choice)

Once those options are set hit Generate. In about 10 seconds you’ll have about 60 classes, 4 projects, and the basis for your starter solution.

Finally let’s get into some TDD. Go to the opened instance of Visual Studio with the solution that was created for you. The unit test project that you created will already have references to the various other projects (including a raft of unit tests to test persistence of your Ernie class). Let’s go back and start our Customer test again, except we’ll do things a little differently.

First let’s modify the test that we originally wrote. Instead of expecting an exception, we’ll use an IsValid boolean property:

namespace NetTiersTDD.UnitTests

{

    [TestFixture]

    public class CustomerFixture

    {

        [Test]

        public void CustomerNameShouldNotExceed50Characters()

        {

            Customer customer = new Customer();

            customer.Name = "X".PadLeft(51);

            Assert.AreEqual(false, customer.IsValid);

        }

    }

}

Looks harmless and something we might do. IsValid will return true or false if the customer object is valid (and in this test, we want IsValid to return false because we expect it to be that way after setting the name with too many characters). Now here’s where the magic comes in. Create your Customer class but inherit from a class called EntityBase (a class that’s part of the framework NetTiers created). To get this code setup to compile, EntityBase requires a few abstract methods to be implemented. They don’t have to do anything (for now) so you have a couple of options (like create a StubEntityBase and implement the methods in that). So let’s do that make our Customer class work. Create a class called StubEntityBase with these members implemented like so:

public class StubEntityBase : EntityBase

{

    public override void CancelChanges()

    {

        throw new Exception("The method or operation is not implemented.");

    }

 

    public override string TableName

    {

        get { throw new Exception("The method or operation is not implemented."); }

    }

 

    public override int ID

    {

        get

        {

            throw new Exception("The method or operation is not implemented.");

        }

        set

        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

 

    public override string Title

    {

        get

        {

            throw new Exception("The method or operation is not implemented.");

        }

        set

        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

 

    public override string EntityTrackingKey

    {

        get

        {

            throw new Exception("The method or operation is not implemented.");

        }

        set

        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

 

    public override object ParentCollection

    {

        get

        {

            throw new Exception("The method or operation is not implemented.");

        }

        set

        {

            throw new Exception("The method or operation is not implemented.");

        }

    }

 

    public override string[] TableColumns

    {

        get { throw new Exception("The method or operation is not implemented."); }

    }

}

Now inherit Customer from StubEntityBase instead of EntityBase.

public class Customer : StubEntityBase

{

    private string _name;

    public string Name

    {

        set

        {

            _name = value;

        }

        get

        {

            return _name;

        }

    }

}

Great, run the unit test and it fails. IsValid returns true because we haven’t put any validation checking in our business object yet. Now it’s time to make it pass. Change the Customer class to show this:

public class Customer : StubEntityBase

{

    protected override void AddValidationRules()

    {

        ValidationRules.AddRule(

            Validation.CommonRules.StringMaxLength,

            new Validation.CommonRules.MaxLengthRuleArgs("Name", 50));

    }

 

    private string _name;

    public string Name

    {

        set

        {

            _name = value;

            OnPropertyChanged("Name");

        }

        get

        {

            return _name;

        }

    }

}

We’re doing two things in the business class to make our test pass. First, we’re overriding a method called AddValidationRules. This gets called (lazy loaded) when a validation check is done (for example when you call IsValid). The ValidationRules is a list of rule objects (again, classes in the NetTiers framework) that you can setup. The CommonRules contains things like length checks, required fields, not greater than, etc. You can also get into designing your own rules but that’s for another blog. So we tell it to check the property named “Name” and make sure it doesn’t exceed 50 characters. If it does, it adds a BrokenRule object to another list.

Then we add a call to the setter for name to trigger an event called “OnPropertyChanged” and pass the name of the property we’re setting. This will do a validation check against the property by invoking any rules that match the property name (you can have multiple rules per property).

Finally when the IsValid property is called on the business entity, it checks the ValidationRules list, finds a list of BrokenRules (rules that failed validation when the OnPropertyChanged event was called) and returns true of the count is not 0.

Cool huh? Lots of business validation stuff going on but none of the plumbing we had to write to get it. There are some other neat side effects that you can get from this framework. For example, back in our unit test we can just ask the object what the error(s) are and print them out:

[Test]

public void CustomerNameShouldNotExceed50Characters()

{

    Customer customer = new Customer();

    customer.Name = "X".PadLeft(51);

    if (!customer.IsValid)

        Console.WriteLine(customer.Error);

    Assert.AreEqual(false, customer.IsValid);

}

This prints out:

------ Test started: Assembly: NetTiersTDD.UnitTests.dll ------

Name can not exceed 50 characters

1 passed, 0 failed, 0 skipped, took 0.56 seconds.

Very useful when writing out the errors to the screen or a log (rather than having to figure out what the error was, then compose a message for it).

Let’s go back to what was originally generated with that first cut off the database. By default, NetTiers generates a report showing you all the craziness it did. This includes a plethora of classes like ListBase, EntityFactory, BrokenRule, ValidationRules, EntityCache, EntityPropertyComparer, and a bunch of other classes. These classes actually have nothing to do with data persistance but form an object framework that we can use (like above) to validate our business objects.

These are all generated by the tool but are used by business entities. For example, ListBase is a generic list (just like List<T>) but supports searching, filtering, sorting, and databinding. Feel free to load up business entities into it and bind it to a GridView for some fun. EntityCache is a class that you can use to add entities to a cache manager. The manager is actually driven by Enterprise Libraries (which the NetTiers code is built on top of) but it lets you add your own business entities to a cache and find them later. This can be handy for in-memory Repositories or just quick and dirty tests where you need to retrieve known objects (but don’t want to create singletons or something). There’s lots of great classes and helpers that are generated for you and all it takes is a single table to get going (which you can delete later and all the code for it will wash away) so explore the business entity project that gets created and check it out (there’s also full documentation on all the classes and methods that you can create using something like Sandcastle).

Finally, at some point you will (or probably) have to save your business entities data in a database. Again, this is where it gets simple (but it’s a bit of a manual process). Let’s say you’ve written up your object with various rules (not just length checking stuff but real business tests) and need to save it. You take a look at what the business object has and decide on a structure. Just a simple structure since all you really need to do is CRUD. Create the table and regen the DAL using CodeSmith and NetTiers (saving your business validations before you do this). Now just drop the business validations in the new genrated object that will derive from MyObjectBase and you’re good to go.

Let me clarify the steps I described above:

  • Write your unit tests and create whatever business classes that come out of your tests (adding your business classes to the Entities project NetTiers generated)
  • Identify what properties you need to persist from your business class and create a simple table with the same name as the class to hold them
  • Rename your business class to a placeholder (like MyBusinessObject.txt)
  • Generate the DAL from the database
  • The Entities project will contain one business class for each table created. This class is split into two parts. MyBusinessObject and MyBusinessObjectBase. MyBusinessObject.cs is never regenerated so you’re free to modify it with your business logic and validations (plus any properties that are not stored in the db). MyBusinessObjectBase is a generated file that will get recreated each time the code gen runs (and update accordingly if you add/remove columns in the database)

For example I create a Customer class that will hold all my customer info. I decide that I’m going to persist the FirstName, LastName, and Age properties. I create a table named Customer to hold these (putting whatever data length and range validations I want on them). Then I rename Customer.cs to a placeholder like Customer.txt. I regen the DAL using NetTiers. I then take the generated Customer class (that will not get overwritten) and drop in the contents of Customer.txt (the part that does my business rules validations).

You’ll probably do two things with the generated business class. First is to override the AddValidationRules method and add any non-data related rules. Second is to write any business domain rules methods and properties that are not stored in the database (like calculated values). Again this class is not overwritten when you regenerate your code, but the generated code will stay in sync with your database adding new properities and modifying data integrity rules along the way (say if you change the length of a field).

The cool thing is that things like the length validations (on properties that are persisted) will already be done for you based on your settings in the database. Go ahead and check out the MyBusinessObjectBase.generated.cs file and you’ll see it’s already written out the AddValidationRules method based on columns in the database (remember to call the base method if you add your own rules in the business class).

Okay, this approach isn’t perfect. It’s not true TDD as in only writing what’s needed. However what does System.Object give you? A bit fat nothing except a unique GetHashCode method and a couple of other useless things. Why not build objects on top of something that has a little extra for you (without having to write it yourself). You do have to make some tradeoffs and for some, it’s too painful to do this. First, you have to bite the bullet that you have a 1:1 relationship of object to table. That’s just the way the NetTiers templates work. Second, you have to have a table (initially and eventually) to generate the code that will do all the data validation but then most business entities have to wind up stored somehow. This technique just helps it along.

You do also have to build your system on top of Enterprise Libraries and some people are not willing to make that leap just yet. Also, some TDD purists will say that you shouldn’t be dependent on this kind of framework but only write what you need when you need it. I agree but this (IMHO) helps. Look at Rocky Lhotka and his CSLA.NET framework. It’s a highly successful business object framework that you could do TDD from. This isn’t that much different from it (other than the fact you need a database to start from). Maybe I’m wrong and this approach is all wrong, but you need to decide that, not some book or blog entry on the interweb.

Finally though, as I mentioned, after all this is said and done I can have my business objects persist with a few lines of code and leverage a validation framework to boot. To me, that’s worth the cost of a one-to-one relationship of table to object and some trade-offs if it means that I can work faster with my business domain and validate it quickly and easily. YMMV.

NOTE: On reading this over now and thinking about it a little more, you might start with a table called Stub. Add an integer ID field and nothing else to it (CodeSmith and NetTiers need at least one column to create something from). Generate the framework from it and base your business objects off the Stub base class (StubBase) rather than creating your own stub like described above. It’ll work in the interim until you have a table of your own for your business class and in the case where you don’t have a table for a business object, you could use this one. Just a thought anyways.

ADDITIONAL NOTE: Thinking even more about the stub idea, you could probably toss away the Data project and Data.SqlClient project and just be left with the Entities project with a business object framework to leverage, but then that’s really no fun is it?

FINAL NOTE: Okay, took me about 4 hours to put this entry together so hope it tweaks your brain and encourages discussion in the community. Maybe I’m completely off my rocker and just talking out of my butt, or maybe not. Anyways, this is a technique I’m using on a current (Enterprise) project and it’s coming along nicely. A combination of using the Composite Application UI Block, NetTiers, Domain Driven Design, and more Unit Tests than you can shake a stick at and I’m pretty confident the project will be a success in many ways.

4 Comments

  • A very interesting approach. CodeSmith Pro is kinda expensive though, but I guess some would say it is worth the $$$. ;-)

  • A small suggestion - you can improve the last code example by adding a message to your assertion:

    Assert.IsFalse(customer.IsValid, customer.Error);

    However, I'm a little uncomfortable with a post-setter validation approach (and I cetainly don't think performance concerns, on their own, are important enough to force a design change unless proven through profiling). What you end up with are objects that can be in an invalid state. You might not be able to save them to your database in that state, but they are there nonetheless. What if I wish to use one of these objects after setting one of its properties? Must I always call IsValid before every other method call? Do I have to interpret the "Error" string to find out what the problem is?

    Jim

  • @Jim: Agreed that adding the customer.Error to the assert would be more meaningful. I meant you could also have that "user friendly" message be used in the UI if you wanted.

    I hear you on the concerns with the post-setter approach, and I don't think performance is the main concern (even though I mentioned it here). I just think that while maybe business rule voilations are errors, they're not exceptions. I consider exceptions things that happen that are not expected. A user entering the wrong information isn't unexpected in a typical app.

    Once you set a property and it voilates a pre-defined rule you setup, the object is considered invalid. I wouldn't say you have to check it before every method call, but you might want to check it after exposing the object to a source that could have caused it to become invalid and before you need to do something with it. Error will tell you what the problem is.

    I agree it's a pain but then if you're not wrapping your setters in a try/catch block is it really a good practice to have your domain objects fail out the entire application when business rules fail? Some rules might not warrant erroring out the app but you need to know about them.

    Again, all of this depends on the context of the app and situation. No one size fits all works.

  • Great entry. I just started checking out NetTiers and you've done a great job here of showing some of what the generated code does above and beyond the basics in the manual. Looking forward to any more insight you wish to share on your Blog!

    -cheers

Comments have been disabled for this content.