Testing code that uses Dapper 1.10

Dapper is a wonderful and simple .NET library to execute SQL statements against your database, what's now called a Micro-ORM. I have a project that uses Dapper for some specific queries and (as you always should) have it cover by tests.

This was my test code (using Moq):

   1:  public void SomeTest()

   2:  {
   3:      DataTable dt = SetupDummyClassDT();
   4:      _command.Setup(a => a.ExecuteReader()).Returns(dt.CreateDataReader());
   5:     
   6:      string calledCommand = "";
   7:      _command.SetupSet(cmd => cmd.CommandText)
   8:          .Callback(cmd => calledCommand = cmd)
   9:          .Verifiable();
  10:   
  11:      _target.Single<DummyClass>(1);
  12:   
  13:      Assert.AreEqual("select * from dummyclass where Id = @Id", calledCommand);
  14:  }

_command is a Moq object and _target is my class under test (a repository class that takes care of creating the SQL command for the class)… This was all ok until this morning that I upgraded to Dapper 1.10 and some of my tests broke. Why!!

The problem, turns out, was that internally dapper needs to add parameters to the IDbCommand.Parameters collection and this was null in my IDbCommand stub (_command), so stubbing the Parameters collection takes care of that; but Dapper creates the parameters calling CreateParameter on the IDbCommand, so we need to stub that too to return a new IDbDataParameter, and problem solve. My test ended up like this:

public void SomeTest() {
    // setup
    DataTable dt = SetupDummyClassDT();
    _command.Setup(a => a.ExecuteReader()).Returns(dt.CreateDataReader());

    // we need to setup the parameters list and the CreateParameter method
    // so dapper can do its magic without crashing
    Mock<IDataParameterCollection> paramsList = new Mock<IDataParameterCollection>();
    _command.SetupGet(a => a.Parameters)
        .Returns(paramsList.Object);
    _command.Setup(a => a.CreateParameter())
        .Returns(new Mock<IDbDataParameter>().Object);

    string calledCommand = "";
    _command.SetupSet(cmd => cmd.CommandText)
        .Callback(cmd => calledCommand = cmd);

     // act
    _target.Single<DummyClass>(1);

    // assert
    Assert.AreEqual("select * from dummyclasses where id = @id", calledCommand);
}

This is really knowing too much about the internals of Dapper, but that's how I ended up. An alternative approach to test this would be to create a virtual method Query<T> in the repository that calls Dapper's Query<T> method, and in testing using a subclass of the repository and override that method to get the command that I'm trying to execute.

No Comments