SourceForge Logo

Mock ADO.NET Framework

So what is the Mock ADO.NET Framework?  This framework is a series of classes that implement the core ADO.NET interfaces as well as the IDbConnectionFactory interface.  With these implementations, an .NET programmer can completely test their data access code without actually accessing the database!  The ideas for this were taken from Wiki MockObject.  Let's look at an example to see this is done...

Example

Let's assume we have the following object -

public class Person {
   public static PersonCollection GetPeopleById(Int32 id) {
      PersonCollection col = new PersonCollection();

      using(IDbConnection conn = DbConnectionFactories.Default["mydbfactory"].CreateConnection()) {
         IDbCommand cmd = conn.CreateCommand();
         cmd.CommandText = "select * from person where id = ?";

         IDataParameter param = cmd.CreateParameter();
         param.DbType = DbType.Int32;
         param.Direction = ParameterDirection.Input;
         param.Value = id;

         cmd.Parameters.Add(param);

         conn.Open(); // don't open till we need it

         using(IDataReader rdr = cmd.ExecuteReader()) {
            while(rdr.Read()) {
               //
               // fill up col
               //
            }
         }
      }

      return col;
   }
}
     
We've all seen code like this.  Most of us have written code like this.  So how do we test it?  If you are lucky, you can run it against a private database on your local development box.  This allows you to ensure the results of the test.  If you are unlucky, you have to hit a database being used by many different people.  Anybody working with Oracle probably has this problem :-).  

So how do we test it?  Luckily, the developer who wrote this code used the ADO.NET interfaces as well as the factory structure set up by Abstract ADO.NET.  Using the Mock framework, the developer can write a test case (assuming NUnit) that looks like this...

public class TestPerson : TestCase {

   public TestPerson(String n) : base(n) {

   }

   public void TestGetById() {
      MockConnectionFactory factory = new MockConnectionFactory();
      MockConnection connection = new MockConnection();
      MockCommand command = new MockCommand();
      MockParameter param = new MockParameter();
      MockReader reader = new MockReader();

      // set up the reader
      reader.SetExpectedData(GetData()); // just return one or more DataTables that contain your simulated results

      // set up the parameter
      param.ExpectedDbType = DbType.Int32;
      param.ExpectedValue = 10;

      // set up command
      command.ExpectedCommandText = "select * from person where id = ?";
      command.ExpectedConnection = connection;
      command.ReaderResult = reader;
      command.SetExpectedParameters(new MockParameter[]{param});

      // setup connection
      connection.SetExpectedCommand(command);

      // setup factory
      factory.SetExpectedConnection(connection);

      DbConnectionFactories.Default.Add("mydbfactory", factory);

      ArrayList col = Person.GetPeopleById(10);

      //
      // Validate col has the data that was returned from GetData
      //

      factory.Verify();
      connection.Verify();
      command.Verify();
      param.Verify();
      reader.Verify();
   }

   private DataTable GetData() {
      DataTable table = new DataTable();
      table.Columns.Add("id", typeof(Int32));
      table.Columns.Add("name", typeof(String));

      DataRow row = table.NewRow();

      row[0] = 10;
      row[1] = "Justin Rudd";

      table.Rows.Add(row);

      table.AcceptChanges();

      return table;
   }
}
     
We can fully simulate the exact pattern that the People component was expecting.  We can even simulate all the error conditions that we might expect to happen.  Such as connection failing to open, ExecuteReader failing, etc.  

So what do the calls to Verify do at the end of the test code?  They test certain conditions that were setup by the tester.  For example, MockCommand.Verify will verify that the command text is what you expected, that the connection that it is holding really is the one you were expected, etc.  MockConnection.Verify will make sure that the connection is closed at the end.  MockReader will verify that the amount of data you expected to be read was read (e.g. number of rows).