- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试对一些使用 Entity Framework 6 的 Web Api 2 Controller 进行单元测试,但在添加实体后加载相关实体时遇到问题。我正在使用 Moq 创建模拟的 DbContext 和 DbSet,并添加了
public virtual void MarkAsModified<T>(T item) where T : class
{
Entry(item).State = EntityState.Modified;
}
绕过 Put 操作的 _db.Entry(foo).State = EntityState.Modified;
问题。
在这个简化示例中,Api 操作是一个帖子,我们需要在其中取回 2 个相关实体(Bar 和 Qux)。
[ResponseType(typeof (Foo))]
public async Task<IHttpActionResult> PostFoo(Foo foo)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
//Do other stuff
_db.Foos.Add(foo);
_db.Entry(foo).Reference(x => x.Bar).Load();
_db.Entry(foo).Reference(x => x.Qux).Load();
await _db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new {id = foo.Id},foo);
}
然后一个简化的测试将是
[TestMethod]
public async Task PostFoo()
{
var model = new Foo
{
Name="New Foo",
QuxId = 99,
Qux = null,
BarId = 66,
Bar = null
};
var result = await _controller.PostFoo(model) as CreatedAtRouteNegotiatedContentResult<Foo>;
Assert.IsNotNull(result);
Assert.IsNotNull(result.Qux);
Assert.IsNotNull(result.Bar);
}
是否有一种更模拟友好的方式来执行 _db.Entry(foo).Reference(x => x.Bar).Load();
最佳答案
关于解决方案的总体思路可以在这里看到
Mocking Entity Framework when Unit Testing ASP.NET Web API 2: dependency injection
目前,您的 Controller 与 EF 的耦合过于紧密,因此我的建议是从 Controller 中抽象出 DbContext 和 DbSet 依赖项,以便模拟友好。
为了解决 _db.Entry(foo).Reference(x => x.Bar).Load()
这里是基于您在您的应用程序中使用的依赖操作的简化抽象发布
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();
}
并允许具体的实现能够做到这一点。
public void LoadRelatedEntity<T, TRelated>(T item, Expression<Func<T, TRelated>> exp)
where T : class
where TRelated : class
{
_db.Entry(item).Reference(exp).Load();
}
现在可以将此依赖项注入(inject)到 Controller 中,也可以对其进行模拟。
这是一个潜在 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);
}
}
从那里开始,只需创建您的测试即可。
[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();
}
希望对你有帮助
关于c# - 使用 Web Api 2 Controller 测试的 Moq 在模拟 DbContext 中加载相关实体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34515925/
我是一名优秀的程序员,十分优秀!