I wanted a way to easily guard my database-specific unit tests. Using the new System.Transactions namespace and ADO.NET's auto-enlist features did the trick. Note, this has only been tested against SQL Server 2000 and needs DTC in that scenario. This should easily work with SQL Server 2005 through the lightweight transaction manager as well (2005 defers promoting transactions as long as possible, relying on the LTM instead).
Code:
public class TransactionalTest
{
public TransactionalTest()
{
Transaction.Current = new CommittableTransaction();
}
~TransactionalTest()
{
if (Transaction.Current != null && Transaction.Current.TransactionInformation.Status != TransactionStatus.Committed)
Transaction.Current.Rollback();
}
}
My test code then looks like:
Code:
[TestFixture]
public class AccountTest : TransactionalTest
{
[Test]
public void CreateAccount()
{
Account user = new Account();
user.Username = "test";
user.Password = "test";
Role adminRole = Role.Get(1);
user.Roles.Add(adminRole);
user.Save();
Assert.IsTrue(user.Id > 0);
Assert.IsTrue(user.Version == 1);
}
}
The user.Save() function, effectively, looks like this:
Code:
BeginTransaction();
Session.SaveOrUpdate(o);
CommitTransaction();
With the call to begin and commit transaction being convenience wrappers for session.begintransaction and session.committransaction.
If you run your test with show_sql=true, you'll notice that the SQL does execute and no errors are thrown. You should be able to verify that no data is saved in the database.
If you absolutely wanted to force a save to the database in a specific instance, but still extend this base class for all other tests in given suite, then you could run this:
Code:
[TestFixture]
public class AccountTest : TransactionalTest
{
[Test]
public void CreateAccount()
{
Account user = new Account();
user.Username = "test";
user.Password = "test";
Role adminRole = Role.Get(1);
user.Roles.Add(adminRole);
user.Save();
((CommittableTransaction)Transaction.Current).Commit();
Assert.IsTrue(user.Id > 0);
Assert.IsTrue(user.Version == 1);
}
}
Known limitations: I haven't tested this against any other platforms yet. I'm still new to the whole System.Transactions namespace so I'm not quite sure what the model would look like against, say, MySQL. I'm guessing it'd try and promote to the DTC as well, but that's just a hunch. Additionally, if you are trying to run your test against a remote 2000 SQL Server, the DTC is set to refuse remote connections by default with the SP4 install. You'll receive a very clear error message to this effect as well. Set the DTC on the remote box to allow remote clients and you'll be good to go.