Chapter 12. Testing database code

Even for projects where the database is used just as a simple persistence layer, it has an impact on automated tests. Integration and acceptance tests should run in an environment as close to the production environment as possible, which today often involves a database. This means that data needs to be set up before the test, cleaned up after, and that changes to data may need to be verified in the database. Writing code to do this in .NET is not rocket science, but it is dull and error-prone, and I'd rather avoid it.

DbFit is an extension library to FIT that enables tests to be executed directly against a database. DbFit fixtures take care of all the database integration plumbing, including automated transaction management, parameter declarations and selecting the right column or parameter type. Because of this, it is easier to write database tests with DbFit than it is to implement manual validations.

To use DbFit fixtures in your tests, download the dbfit-dotnet-binaries package from http://sourceforge.net/projects/dbfit. To install it, unpack the ZIP and copy dbfit.dll into the folder with the .NET test runner. If you installed FitNesse as suggested in Setting up FitNesse, this is the dotnet2 folder in your main FitNesse directory.

[Note]Version issues

While I was working on the second edition of this book, DbFit was still compiled against the old Fitnesse.NET libraries, not FitSharp.

Connecting to the database

DbFit fixtures can work in two modes:

  • In flow mode: a DatabaseTest fixture controls the whole page and coordinates testing. You can use other fixtures as well, but no other fixture can take over flow mode processing. In flow mode, DbFit automatically rolls back the current transaction at the end to make tests repeatable, and provides some additional options such as inspections of stored procedure error results.

  • Standalone: you can use individual fixtures without having DatabaseTest coordinate the whole page. In this case, you are responsible for transaction management. This enables you to have more control over the database testing process, and even supply your own database connection to make sure that .NET integration tests are running in the same transaction.

The mode in which you are using DbFit fixtures affects how you connect to the database.

[Tip]Which mode should I use?

If you can, use flow mode. It gives you automatic transaction management and some other shortcuts. If your test relies on some other fixture controlling the page in flow mode, use standalone fixtures. The syntax is, in most cases, absolutely the same.

Connecting in flow mode

In flow mode, the current database connection is kept in a protected field of the DatabaseTest instance. SqlServerTest is a subclass of DatabaseTest that just initialises it to work with SqlServer 2005.

Use the Connect method to initialise the database connection. Pass the server (optionally followed by the instance name), username, password and the database name as arguments. This is how I connect to a SqlServer 2005 Express[32] instance on my laptop:

!|dbfit.SqlServerTest|

!|Connect|LAPTOP\SQLEXPRESS|FitNesseUser|Password|TestDB|

If you are connecting to a default database, you can omit the fourth parameter. If you want to use non-standard connection properties, or initialise your connection differently (for example, using Windows integrated authentication), call Connect with a single argument — the full .NET connection string. Here is an example:

|Connect|data source=Instance;user id=User;password=Pwd;database=TestDB;|

For flow mode to work correctly, the SqlServerTest fixture must be the first one on the page — not even import can be before it. This is why we explicitly specify the namespace.

Connecting in standalone mode

In standalone mode, the connection properties are stored in the public DefaultEnvironment singleton field inside dbfit.DbEnvironmentFactory. You can initialise it from your own fixtures if you want to pass an existing database connection (to make sure that your .NET tests are using the same transaction as DbFit fixtures). Alternatively, you can use the DatabaseEnvironment fixture from the dbfit.fixture package to define the connection. To change the default environment (or initialise it for the first time), pass the new environment type as the first argument to the fixture. For SqlServer 2005, the value of this argument should be SQLSERVER. DatabaseEnvironment is a SequenceFixture (see Can I use flow mode without weird method names?) that wraps the DefaultEnvironment singleton as a system under test, so that you can then call all its public methods directly — including the Connect method explained earlier.

|import|
|dbfit.fixture|

!|DatabaseEnvironment|sqlserver|
|Connect|LAPTOP\SQLEXPRESS|FitNesseUser|Password|TestDB|

Notice that there is no space between DatabaseEnvironment and Connect — they have to be in the same table. Because we are not using flow mode, we can use the import fixture as well. Most DbFit fixtures are in dbfit.fixture namespace, so it is good practice to include this namespace.

[Tip]Can I use both modes in the same test suite?

Yes, in different tests. Note that the imported namespace may give you some problems in flow mode. If you want to mix and match, then either do not import the dbfit.fixture namespace for standalone tests, or use the utility Export fixture to cancel the namespace import after the standalone test.

!|dbfit.util.Export|
|dbfit.fixture|

Transaction management

In flow mode, the current transaction is automatically rolled back at the end of the page. If you want to commit it to make changes permanent, put the Commit table into the page. There are no arguments or additional parameters — the table contents contain just this one word. Likewise, you can roll back manually in your test using the Rollback table.

In standalone mode, use the DatabaseEnvironment fixture again, but do not specify a fixture argument. This tells the DatabaseEnvironment to use the current default database connection, without attempting to initialise it. Call Commit or Rollback in the second row.

!|DatabaseEnvironment|
|rollback|

It is a very good idea to put this table in a TearDown page for your test suite when you use standalone DbFit fixtures. This will make sure that your tests are repeatable.

[Important]Fixtures and methods

All the fixtures described in the rest of this chapter are in the dbfit.fixture namespace. In flow mode, do not use the fixtures directly, but instead call methods of the DatabaseTest class. The appropriate methods have the same names as the fixtures they relate to. If you import the namespace for standalone fixtures, the table syntax in both modes is absolutely the same.

[Tip]Can I use DbFit with a different database?

In this book, I use Sql Server 2005 examples because this is what most people use with .NET. DbFit also supports Oracle 9 and later versions and MySql 5 (only in the Java version) out of the box. If your database is not one of these, you can still use DbFit with a bit of effort.

DbFit fixtures use an abstraction for the database engine represented by the dbfit.IDBEnvironment interface. This interface is relatively simple and it should not take you more than four to five hours to implement it for your particular database. DbFit is open-source so you can even take a peek at SqlServerEnvironment and OracleEnvironment implementations to help you get started with the task.

[Note]Stuff to remember
  • DbFit fixtures can work in flow mode or in standalone mode. Flow mode automatically controls transactions and has some other helpful shortcuts. Standalone mode gives you more control.

  • Use the Insert fixture to prepare the stage quickly for data-driven .NET integration tests.

  • Use the Query fixture to verify the database state after a data-driven .NET integration test or the execution of a stored procedure.

  • Use the ExecuteProcedure fixture to test stored procedures or quickly script a stored procedure call for a .NET integration test.

  • Use the Execute fixture to execute any database statement.

  • FitNesse symbols are automatically mapped to database bound variables.



[32] free version of SqlServer 2005 for developers. See http://www.microsoft.com/sql/editions/express/.