6

Is it possible to mock the enterprise library 5 version of 'Database'? If so... how?

There is no IDatabase interface (which is a mystery as I though Microsoft P&P would be more on the ball about testability benefits of exposing such an interface).

I have a Repository class which used EntLib 5 Data Access Application Block.

I am retro fitting unit tests into this class and need to mock out the dependency on the Database object. This class is now passed the Database via its constructor and uses a Database object to perform operations on the Db.

I use the following to resolve an instance of the Database to be passed to my Repository:

Container.RegisterType<IFooRepository, FooRepository>(
    new InjectionConstructor(
        EnterpriseLibraryContainer.Current.GetInstance<Database>("FooDbConnStr")
    )
);

I don't wish these unit tests to become integration tests.

I have tried using Moq to create a dynamic mock of the Database type, but this has proved tricky as Database requires a connection string and a DbProviderFactory in its constructor. Maybe if there was such a thing as a MockDbProviderFactory.

This is the form that the unit test is taking:

EntLib UnitTest Attempt to Mock Database

Aside: I also find the use of a static logger class very difficult to test. Hopefully I am missing some trick here, but I must say I am disappointed with testability thus far.

holsee
  • 1,974
  • 2
  • 27
  • 43
  • Sorry about my ignorance, but as you have an interface for your repository, wouldn't be easier to mock the IFooRepository instead of database? – thiagoleite Jul 13 '12 at 17:24
  • 1
    I needed to mock the EntLib Database instance in this scenario in order to unit test the implementation of IFooRepository in isolation. – holsee Jul 17 '12 at 22:06

4 Answers4

3

FWIW, I was able to mock a SqlDatabase using Moq. SqlDatabase has a SqlClientPermission attribute which does not play well with Castle Windsor (used by Moq). I had to explicitly instruct Castle to ignore the SqlClientPermission attribute to get the test to work (see line 1 in the example below). Below is a sample unit test (borrowing Steven H's example).

    [TestMethod]
    public void FooRepo_CallsCorrectSPOnDatabase()
    {
        Castle.DynamicProxy.Generators.AttributesToAvoidReplicating.Add(typeof(System.Data.SqlClient.SqlClientPermissionAttribute));
        var mockSqlDb = new Mock<SqlDatabase>("fake connection string");
        mockSqlDb.Setup(s => s.GetStoredProcCommand("sp_GetFoosById"));
        var sut = new FooRepository(mockSqlDb);
        sut.LoadFoosById(1);
        mockSqlDb.Verify(s => s.GetStoredProcCommand("sp_GetFoosById"), Times.Once(), "Stored Procedure sp_GetFoosById was not invoked.");
    }
Ganga
  • 45
  • 2
  • 7
2

I used FakeItEasy http://code.google.com/p/fakeiteasy/.

I created a mock of SqlDatabase (inherits from Database with a friendlier constructor) passed it to the FooRepostory, called the function under test and asserted expected calls that were made to the Database.

[Test]
public void FooRepo_CallsCorrectSPOnDatabase()
{
    var mockDb = A.Fake<SqlDatabase>(x => x.WithArgumentsForConstructor(new object[] { "fakeconnStr" }));
    var sut = new FooRepository(mockDb);
    sut.LoadFoosById(1);
    A.CallTo(() => mockDb.GetStoredProcCommand(Db.SProcs.GetFoosById)).MustHaveHappened(Repeated.Once);
}
holsee
  • 1,974
  • 2
  • 27
  • 43
1

Database is an abstract base class, and DbProviderFactory is also abstract, so you can mock them both out. As long as you mock out the operations that you're calling on the Database type (just about everything there is virtual so you should be ok there) you don't actually need to do anything in the provider factory. And the connection string can just be empty or null or whatever.

Chris Tavares
  • 29,165
  • 4
  • 46
  • 63
1

I personally loaded up the source code and used ReSharper to Extract Interface for the Database object. It rebuilt and I used my custom binaries. Wala - An interface! Hint: Interfaces are simple to mock. Why Microsoft P&P group didn't do this, I do not know.

Joshua Ramirez
  • 422
  • 3
  • 10