I\'m trying to unit test some Web Api 2 Controllers that use Entity Framework 6 but having issues with the loading of the related entities after the entity has been added. I\'m
The general idea about the solution can be seen here
Mocking Entity Framework when Unit Testing ASP.NET Web API 2: dependency injection
Currently, your controller is coupled too tightly to EF so my advice would be to abstract the DbContext and DbSet dependency out of the controller so that it can become mock-friendly.
To get around _db.Entry(foo).Reference(x => x.Bar).Load()
here is a simplified abstraction of the dependent actions based on what you are using in your post
public interface IUnitOfWork {
void Add<T>(T item) where T : class;
void MarkAsModified<T>(T item) where T : class;
void LoadRelatedEntity<T, TRelated>(T item, Expression<Func<T, TRelated>> exp)
where T : class
where TRelated : class;
Task SaveChangesAsync();
}
and allow a concrete implementation to be able to do this.
public void LoadRelatedEntity<T, TRelated>(T item, Expression<Func<T, TRelated>> exp)
where T : class
where TRelated : class
{
_db.Entry(item).Reference(exp).Load();
}
This dependency can now be injected into the controller and can also be mocked.
Here is a simplified version of a potential controller
public class FooController : ApiController {
IUnitOfWork unitOfWork;
public FooController (IUnitOfWork uow) {
this.unitOfWork = uow;
}
[ResponseType(typeof(Foo))]
public async Task<IHttpActionResult> PostFoo(Foo foo) {
if (!ModelState.IsValid) {
return BadRequest(ModelState);
}
//Do other stuff
unitOfWork.Add(foo);
await unitOfWork.SaveChangesAsync();
//Load related entities
unitOfWork.LoadRelatedEntity(foo, x => x.Bar);
unitOfWork.LoadRelatedEntity(foo, x => x.Qux);
return CreatedAtRoute("DefaultApi", new { id = foo.Id }, foo);
}
}
From there it's just a matter of creating your test.
[TestMethod]
public async Task TestPostFoo() {
//Arrange
bool saved = false;
var model = new Foo {
Name = "New Foo",
QuxId = 99,
Qux = null,
BarId = 66,
Bar = null
};
var mockUnitOfWork = new Moq.Mock<IUnitOfWork>();
mockUnitOfWork.Setup(x => x.SaveChangesAsync())
.Returns(() => Task.FromResult(0))
.Callback(() => {
model.Id = 1;
saved = true;
});
mockUnitOfWork
.Setup(x => x.LoadRelatedEntity<Foo, Qux>(It.IsAny<Foo>(), It.IsAny<Expression<Func<Foo, Qux>>>()))
.Callback(() => model.Qux = new Qux());
mockUnitOfWork
.Setup(x => x.LoadRelatedEntity<Foo, Bar>(It.IsAny<Foo>(), It.IsAny<Expression<Func<Foo, Bar>>>()))
.Callback(() => model.Bar = new Bar());
var controller = new TestsFooApiController(mockUnitOfWork.Object);
controller.Request = new HttpRequestMessage { };
controller.Configuration = new HttpConfiguration();
//Act
var result = await controller.PostFoo(model) as CreatedAtRouteNegotiatedContentResult<Foo>;
//Assert
result.Should().NotBeNull();
result.Content.Should().NotBeNull();
result.Content.Id.Should().BeGreaterThan(0);
result.Content.Qux.Should().NotBeNull();
result.Content.Bar.Should().NotBeNull();
saved.Should().BeTrue();
}
Hope this helps