As I've mentioned, I'm a big fan of ActiveRecord. I like having all of my data-access related code stored in exactly one place. There's no separate mapping file to maintain, and if I'm really lazy, I can even let ActiveRecord generate the schema for me (yeah, that doesn't really work once you have to worry about migrating databases between schema versions, but there's probably a way around that).
Testing code that depends on my ActiveRecord object model can be complicated. The tests require that the underlying database be in a known, consistent state, and that means that I have to use test setup/tear down methods to initialize the database with the schema and/or wipe out data created by other tests. Worse, it means I have to actually have a database accessible for use in the tests! Since this behavior tends to be the same across my ActiveRecord-dependent test fixtures, I have created a base test fixture and a simple utility class that keeps my real NUnit test fixtures very simple and free of ActiveRecord initialize and cleanup code, and it uses an in-memory database so there's no dependency on an external database. First, the utility class:
1: /// <summary>
2: /// Contains various utilty methods and properties
3: /// to aide in the configuration and management
4: /// of ActiveRecord. All objects that are to be persisted
5: /// by ActiveRecord are defined here.
6: /// </summary>
7: public static class ActiveRecordHelper
8: {
9: #region Private Static Fields
10:
11: /// <summary>
12: /// This field holds the types that are to be persisted
13: /// by ActiveRecord. These should be listed bottom-up in order
14: /// by dependency, so use C, B, A if C depends on B, and B depends on A.
15: /// </summary>
16: static readonly Type[] mModelTypes = new Type[]
17: {
18: typeof(Widget)
19: };
20:
21: #endregion
22:
23: #region Public Properties
24:
25: /// <summary>
26: /// Gets all the models that are available for persistence
27: /// in ActiveRecord.
28: /// </summary>
29: public static Type[] ModelTypes
30: {
31: get
32: {
33: return mModelTypes;
34: }
35: }
36:
37: #endregion
38: }
Note how the types used by ActiveRecord are listed in order of dependency. This is important because the test fixture will clean up the database by running DeleteAll commands on the types in that order.
Next is our base test fixture class:
1: /// <summary>
2: /// Provides a base class that database-dependent unit tests can utilize. It
3: /// handles managing the ActiveRecord data layer setup/init, clean up, etc.
4: /// </summary>
5: public abstract class DatabaseDependentTestFixture
6: {
7: #region Protected Methods
8:
9: /// <summary>
10: /// Deletes all data in the database.
11: /// </summary>
12: protected void DeleteAllData()
13: {
14: Type[] types = ActiveRecordHelper.ModelTypes;
15:
16: foreach (Type t in types)
17: {
18: if (typeof(ActiveRecordBase).IsAssignableFrom(t) &&
19: t.GetMethod("DeleteAll", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, new Type[0], null) != null)
20: {
21: t.InvokeMember("DeleteAll", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod, null, null, null);
22: }
23: }
24: }
25:
26: /// <summary>
27: /// Initializes ActiveRecord to work with an in-memory temporary database
28: /// for the RBCMS objects.
29: /// </summary>
30: protected void SetupActiveRecord()
31: {
32: Dictionary<string, string> settings = new Dictionary<string, string>();
33: settings.Add("connection.driver_class", "NHibernate.Driver.SQLite20Driver");
34: settings.Add("dialect", "NHibernate.Dialect.SQLiteDialect");
35: settings.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
36: settings.Add("connection.connection_string", "Data Source=ActiveRecord.dat;Version=3;");
37:
38: InPlaceConfigurationSource config = new InPlaceConfigurationSource();
39: config.PluralizeTableNames = true;
40: config.Add(typeof(ActiveRecordBase), settings);
41:
42: if (ActiveRecordStarter.IsInitialized)
43: {
44: ActiveRecordStarter.ResetInitializationFlag();
45: }
46:
47: ActiveRecordStarter.Initialize(config, ActiveRecordHelper.ModelTypes);
48: }
49:
50: /// <summary>
51: /// Drops and recreates the RBCMS database schema.
52: /// </summary>
53: protected void RecreateSchema()
54: {
55: ActiveRecordStarter.CreateSchema();
56: }
57:
58: #endregion
59:
60: #region Public Methods
61:
62: /// <summary>
63: /// Initializes the data layer for first use.
64: /// </summary>
65: [TestFixtureSetUp]
66: public virtual void TestFixtureSetup()
67: {
68: SetupActiveRecord();
69: RecreateSchema();
70: }
71:
72: /// <summary>
73: /// Wipes out all existing data in the database, allowing
74: /// unit tests to start with a blank slate each time (which
75: /// is what they should be doing anyway).
76: /// </summary>
77: [SetUp]
78: public virtual void TestSetup()
79: {
80: DeleteAllData();
81: }
82:
83: #endregion
84: }
There's quite a bit going on here, so let's walk through it. When inherited by a derived class, the TestFixtureSetup and TestSetup mehods will be called by NUnit automatically. The first method will initialize ActiveRecord, configuring it to use a temporary SQLite database, then populating the database with the schema generated by ActiveRecord. Using SQLite means that the database resides entirely in memory, so I don't have to worry about configuring a separate MS SQL database or something like that. The second method is called by NUnit before each test runs, and it uses the ActiveRecordBase<T>.DeleteAll method to clean out any existing data. Reflection is used because DeleteAll is a static, generic method that must be called on the derived type, not the base type, and there's not a way to do that without reflection unless you couple the code to your models.
The setup methods are declared as virtual, so derived test fixtures can override their behavior if needed. Most of the time though, you can just passively take advantage of the functionality like so:
1: /// <summary>
2: /// Test fixture for <see cref="ActiveRecordLinqContext"/>.
3: /// </summary>
4: [TestFixture]
5: public class ActiveRecordLinqContextTests : DatabaseDependentTestFixture
6: {
7: /// <summary>
8: /// Verifies that LINQ works as expected.
9: /// </summary>
10: [Test]
11: public void LinqTest1()
12: {
13: //First, insert a few widgets.
14: for (int i = 0; i < 10; i++)
15: {
16: Widget model = new Widget {Name = "Test" + i, Description = "Test Description " + i};
17: model.SaveAndFlush();
18: }
19:
20: using (new SessionScope())
21: {
22: ActiveRecordLinqContext context = new ActiveRecordLinqContext();
23:
24: string[] names = (from cm in context.Session.Linq<CostModel>()
25: select cm.Name).ToArray();
26:
27: Assert.AreEqual(10, names.Length);
28:
29: names = (from cm in context.Session.Linq<CostModel>()
30: where cm.Id > 5
31: select cm.Name).ToArray();
32:
33: Assert.AreEqual(5, names.Length);
34: }
35: }
36: }
This is the test fixture from yesterday's post, but all its setup logic is now handled by the base class, freeing you up to write simple, clean tests.