May 07 2008

Castle demo app: ActiveRecord basics and unit testing

Published by gojko at 2:57 pm under articles, tutorials

Castle project is a great .NET enterprise application framework. It helps us develop .NET applications and web sites by providing the plumbing and making it easy to test the most important parts of the system. I’ve decided to build a demo application with the Castle project for an internal training session, to show how easy it is to work with this framework and to demonstrate the best practices. I will post the tutorial in parts on this web site as I develop it. In the first post, we work on the object-relational mapping with ActiveRecord and write unit tests for the database mapping layer.

Here is the stuff that you’ll need to run these examples:

Introducing EvilLink

We’ll build an online bookmarking application, called it EvilLink. Here are the user stories that we will develop:

  1. As a site owner, I want users to register and leave their e-mail address so that I can spam them later (hence the app name).
  2. As a user, I want to record my web links online, so that I can access them from anywhere.
  3. As a user, I want to review the links that I previously recorded so that I can visit the interesting sites.
  4. As a user, I want to edit details and descriptions of the links that I previously recorded, so that I can keep them up to date.


This will be a rather simplistic web application, but it will allow us to try out the most important features of all three main components of the Castle project: the ActiveRecord object-relational mapping tool, the Monorail web model-view-controller framework and the Windsor dependency injection container. We will develop this application with full test support, meaning that our goal is to cover everything below the UI surface with unit tests.

Story 1: User registration

To implement this story, we’ll create a User object, connect it to the database, make sure that the ORM system works, and then build a simple Web site on top of that. During this exercise, we show how straightforward it is to use ActiveRecord. In the next one, we work on the basic layout of a Monorail web site and show the way that controllers handle incoming requests to complete the first user story.

The User object

Let’s first create the User class and related database mappings. Create a new solution for this project in Visual Studio, and open a new project called EvilLink.Database. We’ll put database-related objects there.

ActiveRecord is a software architectural pattern that maps objects directly to table rows, and object properties to table columns. This pattern combines domain logic and data, providing easy persistence but not a very nice separation of responsibilities. For more information on this pattern, see Martin Fowler’s Patterns of Enterprise Application Architecture page.

Under the hood, Castle’s ActiveRecord uses NHibernate, but with almost no configuration. In general, preferring convention over configuration is one of the best things about the Castle framework. Instead of an external mapping file, ActiveRecord requires that we put a few attributes on the class. For start, the entire class should be marked with [ActiveRecord]. Persistent properties should be marked as [Property], and the class must have a numeric primary identifier, marked as [PrimaryKey]. Although it can work on plain CLR classes (the only requirement is a default public constructor without arguments), ActiveRecord also has a few utility base classes that you can extend. ActiveRecordBase supplies a bunch of useful finders and actions. ActiveRecordValidationBase integrates with the Castle component validator framework, and provides instant validation features. That means that you can annotate your properties with validation rules, and the object itself will refuse to save or update the database if it is invalid. It also gives you nice validation error messages if the object is not valid, ready to be displayed to the end users. Standard validators include checking whether an attribute is unique, not empty, formatted like an e-mail and a number of other similar actions. For a full list, see the Castle Components Validator documentation. If you make a mistake in the attribute annotations, you will not get a compilation error. Problems like that don’t even pop up when the classes are loaded, but come up when you try to use the class with a misleading message like “ActiveRecordStarter.initialise call was not executed with the type”. Remember to double-check the public constructor and class and property annotations if you get that exception.

We’ll use this to validate that the username is unique, that the e-mail is in the correct format and that the user’s name is not empty.

All ActiveRecord attributes are in the Castle.ActiveRecord namespace. Validation attributes are in the Castle.Components.Validator namespace. Add references to Castle.Core.dll, Castle.ActiveRecord.dll and Castle.Components.Validator.dll and create the User class:

using System;
using Castle.ActiveRecord;
using Castle.Components.Validator;
namespace EvilLink.Database
{
    [ActiveRecord]
    publicclass User : ActiveRecordValidationBase<User>
    {
        private int id;
        private String username;
        private String password;
        private String email;
        private String name;
 
        public User()
        {
        }
        public User(string name, string username, string password, string email)
        {
            this.username = username;
            this.password = password;
            this.email = email;
            this.name = name;
        }
 
        [PrimaryKey]
        public int Id
        {
            get { return id; }
            set { id = value; }
        }
        [Property]
        [ValidateIsUnique]
        public string Username
        {
            get { return username; }
            set { username = value; }
        }
        [Property]
        [ValidateNonEmpty]
        public string Name
        {
            get { return name; }
            set { name= value; }
        }
        [Property]
        [ValidateNonEmpty]
        public string Password
        {
            get { return password; }
            set { password = value; }
        }
        [Property]
        [ValidateEmail]
        public string Email
        {
            get { return email; }
            set { email = value; }
        }
        public override bool Equals(object obj)
        {
            User us=obj as User;
            if (us == null) return false;
            return us.id == this.id;
        }
        public override int GetHashCode()
        {
            return id.GetHashCode();
        }
        public override string ToString()
        {
            return "Userid=" + id;
        }
    }
}

And that’s more or less everything about the database mapping. ActiveRecord will create all the SQL commands automatically. It can even create the database schema. The only thing left is to point it to the correct database.

Step 2: Verifying that the model works

To verify that our new User object works correctly, let’s write a unit test (of course, some purists will now note that I should be writing unit tests before the production code, but it’s a bit hard to explain things in that order. In any case, the User class is fairly simple and contains no business logic, so it is not such a sin to write the unit test after the class. When we build in more business logic later, we’ll do it properly).

Let’s create a new project for database tests, and call it EvilLink.Database.Test. Add references to the three Castle libraries as in the Database project, then also add a reference to the EvilLink.Database project and to NUnit.Framework.dll and NHibernate.dll (you’ll find them in the dependencies zip file in the Castle release).

To connect the User class to the database, we need to initialise ActiveRecord framework by telling it the database configuration. Castle.ActiveRecord.ActiveRecordStarter class is used for that. We need to call the Initialise method and pass the configuration file and list of record classes. We can load the configuration from an Application.Config file or from an external file. Because we want to run this inside the NUnit test runner, let’s use an external file. Theoretically, we can send a list of record classes to the ActiveRecordStarter, but we can also supply the whole assembly and let it find all relevant classes itself. Here are the two lines that prepare our ORM layer:

IConfigurationSourcesource = new XmlConfigurationSource(
  "ActiveRecordConfig.xml");
ActiveRecordStarter.Initialize(Assembly.GetAssembly(
  typeof(EvilLink.Database.User)), source);

Having a dedicated assembly for record classes will make it easier to load and initialise the records appropriately. Avoid putting a lot of non-record classes in such assemblies, because ActiveRecordStarter will have to analyse all the classes in the assembly.

ActiveRecordStarter contains a bunch of other useful methods, most importantly CreateSchema and DropSchema that will try to create or delete the relevant database objects. I emphasised try because there are some interesting bugs in this that prevent correct initialisation of many-to-many mappings in SQL Server. For now, we’ll use this functionality. Later on, if you find that it starts causing you problems, create the schema yourself. ActiveRecordStarter normally allows you to initialise it only once during an application. For unit tests, we want to drop and recreate the schema before every test to avoid dependencies, so we’ll need to tell it to ignore this security check by calling ResetInitializationFlag.

As most ORM tools, ActiveRecord uses the concept of session scopes to reduce database communication and ensure data consistency. All operations on record objects should be performed within a session scope, and the changes are persisted in the database when the session is flushed or the scope ends. In the context of unit tests, it makes sense to automatically create a scope before tests and discard the scope after tests. To verify persistence functionality, we can flush the session and try to load the objects from the database.

To make testing easier, let’s create a utility class that will take care of all ActiveRecord initialisation and session control. It will also provide a method to flush the session so that we can test persistence. Add this class to the EvilLink.Database.Test project:

using System;
using NUnit.Framework;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Castle.ActiveRecord.Framework.Config;
using System.Reflection;
namespace EvilLink.Database.Test
{
    // not a test fixture, it should not be invoked directly
    public abstract class ARTestBase
    {
        protected SessionScope scope;
 
        [TestFixtureSetUp]
        public void InitialiseAR()
        {
            ActiveRecordStarter.ResetInitializationFlag();
            IConfigurationSource source = new 
              XmlConfigurationSource("ActiveRecordConfig.xml");
            ActiveRecordStarter.Initialize(
               Assembly.GetAssembly(typeof(EvilLink.Database.User)),
              source);
        }
        [SetUp]
        public virtual void SetUp()
        {
            ActiveRecordStarter.DropSchema();
            ActiveRecordStarter.CreateSchema();
            scope = new SessionScope();
        }
        [TearDown]
        public virtual void TearDown()
        {
            scope.Dispose();
        }
        public void Flush()
        {
            scope.Flush();
            scope.Dispose();
            scope = new SessionScope();
        }
       
    }
}

We now need to supply the database configuration to ActiveRecord. Create a file called ActiveRecordConfig.xml in the project, and use this as a template:

<?xml version="1.0" encoding="utf-8" ?>
<activerecord isWeb="false" pluralizeTableNames="true">
<config>
      <add key="hibernate.connection.driver_class"
              value="NHibernate.Driver.SqlClientDriver" />
      <add key="hibernate.dialect"
              value="NHibernate.Dialect.MsSql2005Dialect" />
      <add key="hibernate.connection.provider"
              value="NHibernate.Connection.DriverConnectionProvider" />
      <add key="hibernate.connection.connection_string"
              value="Data Source=.\SQLEXPRESS;Initial Catalog=castletest;
user id=castletest;password=castletest" />
</config>
</activerecord>

This example connects to a SQLExpress database on the local machine (.\SQLEXPRESS) under the credentials of user castletest with the password castletest, and selects castletest as the starting database. So either create that on your local instance, or enter different connection details in the connection_string property. To use a different type of database, see the ActiveRecord configuration reference. Just remember to keep isWeb=”false” pluralizeTableNames=”true” in the activerecord element. The first attribute tells ActiveRecord not to use automatic thread/transaction management and the other makes sure that the User objects are saved in the users table (otherwise you will get an error because User is a keyword in SQL Server). Right click on the file, select properties, and then choose “Copy always” as the “Copy to Output Directory” option to make sure that this configuration file is available at runtime when we need it.

Now, let’s write the unit test. Create a UserTests class and check whether we can register a user and whether the validations work correctly:

using System;
using NUnit.Framework;
using Castle.ActiveRecord;
using Castle.ActiveRecord.Framework;
using Castle.ActiveRecord.Framework.Config;
 
namespace EvilLink.Database.Test
{
    [TestFixture]
    public class UserTests : ARTestBase
    {
        [Test]
        public void TestInvalidFormatEmail()
        {
            User u = new User("Mike Smith", "mike", "mpass123", "mike-mike.com");
            Assert.IsFalse(u.IsValid());
            try
            {
                u.Save();
                Assert.IsFalse(true, "invalid password object saved");
            }
            catch (Exception)
            {
            }
        }
        [Test]
        public void TestNonUniqueUsername()
        {
            User u = new User("Mike Smith", "mike", "mpass123", "mike@mike.com");
            u.Save();
            Flush();
            u = new User("Tom Smith", "mike", "tpass123", "tom@mike.com");
            Assert.IsFalse(u.IsValid());
            try
            {
                u.Save();
                Assert.IsFalse(true, "duplicate username object saved");
            }
            catch (Exception)
            {
            }
        }
        [Test]
        public void TestAddingUsers()
        {
            User u = new User("Mike Smith", "mike", "mpass123", "mike@mike.com");
            u.Save();
            Flush();
            User[] fromDb = User.FindAllByProperty("Username", "mike");
            Assert.AreEqual(1, fromDb.Length);
            Assert.AreEqual("mike@mike.com", fromDb[0].Email);
        }
    }
}

Compile the solution, and run the test with NUnit. It should confirm that the database mapping works correctly.

You can also check the database directly, Mike should be there:

You can download the source code for this example from http://gojko.net/resources/EvilLink_i1.zip

Key stuff to remember:

  • the record class needs to be annotated with the [ActiveRecord] attribute, be public, have a no-parameter public constructor. Otherwise loading fails, but you don’t get an error at that point — you will get an error later mentioning how ActiveRecordStarter.initialise call was not executed with the type. This can be a bit misleading.
  • you need to initialise ActiveRecord and load types or whole assemblies. The best practice is to create an assembly especially for ActiveRecord types and then load that.
  • beware of using DB table/field names that are keywords (for example User in SQL Server 2005); converting table names to plurals helps to avoid some problems, but still beware that such problems can happen.
  • make sure you use ActiveRecordValidationBase if you want validations to work. Having only validation attributes on the properties is not enough.
  • Kill the initialisation flag in unit tests to allow re-initialisation
  • ActiveRecord can generate the schema for you — this is very helpful during initial stages of the project but it may not always work as expected.

Next exercise

In the next part of this tutorial, we complete the first user story by developing a web page that allows people to register. I will post the next part on this web site shortly. You can subscribe to RSS updates to get a notification when the next part is online. If you are in UK, and want to learn more about the Castle project, drop by Skills Matter on the 15th of May for a two-hour workshop on Agile Web development with Castle. We are going for beers after the talk.


Add to Del.Icio.Us bookmarks

20 Responses to “Castle demo app: ActiveRecord basics and unit testing”

  1. Patrick Steeleon 07 May 2008 at 7:45 pm

    While I agree that you should try and avoid naming objects the same as SQL keywords, you may be integrating with a legacy DB and have no choice. In those cases, you can tell ActiveRecord what to use as the table name and enclose it in brackets so you’ll get valid SQL. In your example:

    [ActiveRecord("[User]“)]
    public class User : ActiveRecordValidationBase

  2. Adam D.on 08 May 2008 at 12:33 am

    any reason for using the empty catches?

  3. gojkoon 08 May 2008 at 7:05 am

    Hi Adam,

    I’m using an empty catch to ignore an expected exception during a test. The AssertFalse(true) statement after the operation inside the try block is used to fail the test if the exception does not happen. Is there a better way to do it?

  4. Jonathon Rossion 08 May 2008 at 12:18 pm

    You should use the ExpectedException attribute on your test to verify that the exception was thrown, the test will fail if the exception isn’t thrown.

    e.g.
    [ExpectedException(typeof(FileNotFoundException))]

  5. Caseyon 08 May 2008 at 12:28 pm

    Instead of :
    Assert.IsFalse(true, “duplicate username object saved”);

    Can I suggest:
    Assert.Fail(”duplicate username object saved”);

  6. Caseyon 08 May 2008 at 1:13 pm

    >>You should use the ExpectedException attribute on your test to verify that the exception was thrown, the test will fail if the exception isn’t thrown.<<<

    Generally, no you shouldn’t … ExpectedException is generally considered “a bad thing” these days, as it does not identify where the exception occured, and can therefore give misleading results. The try/catch/fail is far more precise.

    Even NUnit has now moved to Assert.Throws() instead of ExpectedException

  7. Caseyon 08 May 2008 at 1:14 pm

    An example from my current project …

    [Test]
    public void Handler_denys_access_with_unauthenticated_user()
    {
    try
    {
    var request = new DemandAuthorisationRequest {User = string.Empty, DemandFor = “CanAdd”};
    handlerUnderTest.Handle(request);
    }
    catch(SecurityException)
    {
    return;
    }
    Assert.Fail(”Access was allowed to unathenticated user”);
    }

  8. Jay Smithon 08 May 2008 at 3:53 pm

    I had the same question about the ExceptionExpected. Thanks Casey for pointing out an alternative. Just curious though, why is the ExceptionExpected considered a bad practice if that is what you ARE expecting is an exception?

  9. Caseyon 08 May 2008 at 5:11 pm

    @Jay

    For a good example, see Jimmy’s blog http://www.lostechies.com/blogs/jimmy_bogard/archive/2008/03/10/the-many-faces-of-expectedexception.aspx

  10. Adam D.on 08 May 2008 at 5:25 pm

    use “does user exist” mechanism instead of introducing exception handling for your expected behavior

  11. gojkoon 08 May 2008 at 5:57 pm

    @adam
    that would be fine if we were checking if the user exists. In this case, the test is about whether activerecord is going to prevent someone from saving an invalid user. How would you test that without actually invoking the exception?

  12. Adam D.on 08 May 2008 at 10:48 pm

    you would not call user.save until you queried for one by email, in this case. You would not rely on your repository to handle your BL by throwing an “exists” exception.

  13. gojkoon 08 May 2008 at 11:16 pm

    Adam,
    Sorry, I really do not understand the difference between your suggestion and what I implemented in the unit test. I have checked for IsValid before trying the exception, but I still want to validate that the framework will prevent people that do not check for duplicates from saving the user.

  14. Adam D.on 08 May 2008 at 11:54 pm

    This is one level above, that’s the difference. I wouldn’t rely on my repository’s exception generation implement my BL.

    with NHQG help to get rid of criteria syntax, here’s the pseudo code

    if (null!=User.Find(userid == potentialId && email==potentialEmail)) (new User(name, potentialId, “mpass123″, potentialEmail)).Save();
    else //display user already exists

  15. Adam D.on 09 May 2008 at 7:22 pm

    oops. the != should be ==

  16. uluon 17 May 2008 at 12:17 pm

    Hi,

    I’m wondering if it is possible to execute my tests without hitting the database. For example, I want to create an entity and verify that my model class fetches (or ignores) it. Or I want to execute a method and verify that an entity object has been created, without a database roundtrip. Hitting a database may be fine when you do integration testing, but I don’t want to touch it in _every_ test. If you can suggest another pattern, I’ll be happy to try it.

    Another useful application is when you are pretty sure that you have all relevant objects fetched already and you don’t want to fetch them again.

    I know that ISession is some sot of a cache, but the online documentation on AR cache is “to be added”. Could you please pint me in the right direction?

    Thanks
    ulu

  17. gojkoon 17 May 2008 at 2:15 pm

    Hi Ulu,

    ActiveRecord mixes domain logic and storage — so it is not the best choice for more complex applications. You can use NHibernate directly from Castle and use the Repository pattern, for example, to write the code in a way that enables you to test domain code independently of storage.

  18. Federicoon 26 May 2008 at 2:37 am

    Hi,

    I think the text in your TestInvalidFormatEmail() is wrong. It says “password”when it’s checking for email.

    Thanks,

    Federico

  19. Stewon 27 Jun 2008 at 11:25 pm

    I’m trying to follow this tutorial, but having problems. (I’m calling “User” “Client”, but otherwise I’m pretty much the same as the examples given.)

    Here’s the error I get when I try to run NUnit for the first time:

    Scrumpy.Database.Test.Tests.ClientTests.TestAddingUsers:
    Castle.ActiveRecord.Framework.ActiveRecordException : Could not drop the schema
    —-> NHibernate.ADOException : Could not close Npgsql.NpgsqlConnection connection
    —-> System.NotSupportedException : This stream does not support seek operations.
    TearDown : System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
    —-> System.NullReferenceException : Object reference not set to an instance of an object.

  20. gojkoon 28 Jun 2008 at 7:03 pm

    Hi Stew,

    This error message suggests that your schema cannot be dropped as part of the unit test fixture set-up. Maybe some data consistency constraints are preventing you from dropping the client table (eg you have more tables in the schema, but you only loaded the Client table into AR during unit testing). Try to drop all tables in the schema manually before running the test.

Trackback URI | Comments RSS

Leave a Reply