Andrew
Andrew

Reputation: 221

Mock DbContext that simulates a database error

I want to unit test my repository's exception handling code. One of my repository's methods uses the standard property of a DbContext that returns a DbSet, then using its AsQueryable method, adds Where parameters and finally calls ToList to query the database.

I think I need an IQueryable object that works up until the point its ToList extension method is called, at which point an exception is thrown. Then I could mock the property to return a mocked DbSet with a mocked AsQueryable method that returns it. But it won't let me mock the ToList method since it's an extension method.

Help?

Upvotes: 2

Views: 1884

Answers (2)

Andrew
Andrew

Reputation: 221

Here's the working code sample that allows me to test a database failure. Credit to @Igor for the inspiration!

[Fact]
public void MyTest()
{ 
// Doctor up an IQueryable that will throw an exception when its ToList method is called.
List<MyEntity> list = new List<MyEntity>();
list.Add(new MyEntity());
var expectedEx = new Exception();
var queryable = list.AsQueryable().Where(r => ThrowException(expectedEx));

var mockSet = new Mock<DbSet<MyEntity>>();
mockSet.Setup(m => m.AsQueryable()).Returns(queryable);
mockSet.As<IQueryable<MyEntity>>().Setup(m => m.Provider).Returns(queryable.Provider);
mockSet.As<IQueryable<MyEntity>>().Setup(m => m.Expression).Returns(queryable.Expression);

// Create a mock DbContext that will throw an exception when the repository calls the IQueryable's ToList method.
var mockContext = new Mock<MyContext>(this.options);
mockContext.SetupGet(x => x.Database).Returns(new Mock<DatabaseFacade>(new MyContext(this.options)).Object);
mockContext.Setup(x => x.MyEntityListProperty).Returns(mockSet.Object);

var repository = new MyRepository(mockContext.Object);

// Call the method that calls the IQueryable's ToList, which will throw an exception, simulating a database error.
var thrownEx = Assert.Throws<Exception>(() => repository.FindEntities(Guid.NewGuid()));
Assert.Equal(expectedEx, thrownEx);
}

private static bool ThrowException(Exception e)
{
    throw e;
}

Upvotes: 4

Steve Py
Steve Py

Reputation: 34673

When it comes to unit testing and repositories, the repository should be the boundary for the mocking, meaning you mock the repository to test the code logic that relies on the repository. (Rather than trying to deal with mocking a DbContext) The repository itself should have no business logic value to test, serving only as the conduit to the data. The behaviour of the repository itself can be tested using a less frequently run integration test, which are test methods run with an actual test data source. (In-memory database or a database instance representing a known state for the tests). This way business logic unit tests can be run frequently, prior to check-ins, or periodically while developing, since they will be kept fast, while more thorough end-to-end integration tests can be run less frequently such as during integration runs prior to builds.

With the repository as the mocked boundary your test mocks just need to return instances of the entities expected to test the code behaviour, empty lists, or throw exceptions, etc. depending on the behaviour scenarios you are covering.

Upvotes: 1

Related Questions