我目前正在尝试最小化我的
Cafe
Get
方法,如果找不到咖啡馆ID,它将抛出ArgumentNullexception
。
System.NotSupportedException:不支持的表达式:x => x.Cafe 不可覆盖的成员(此处:Context.get_Cafe)不得在设置/验证表达式中使用。
发生这种情况是因为最小起订量无法处理其中一个设置表达式吗?
[Fact]
public async Task GetCafeByIdAsync_Should_Throw_ArgumentNullException()
{
var cafe = new List<Cafe>()
{
new Cafe { Name = "Hanna", CafeId = 1},
new Cafe { Name = "Bella", CafeId = 2 }
}.AsQueryable();
var mockSet = new Mock<DbSet<Cafe>>();
mockSet.As<IQueryable<Cafe>>().Setup(m => m.Provider).Returns(cafe.Provider);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.Expression).Returns(cafe.Expression);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.ElementType).Returns(cafe.ElementType);
mockSet.As<IQueryable<Cafe>>().Setup(m => m.GetEnumerator()).Returns(cafe.GetEnumerator());
var mapper = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfile());
}).CreateMapper();
var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Cafe).Returns(mockSet.Object); //failing here
var cafeService = new CafeService(contextMock.Object, mapper);
await Assert.ThrowsAsync<ArgumentNullException>(() => cafeService.Get(2));
}
public async Task<VersionResponse> Get(int cafeId)
{
var cafe = await _context.Cafe.Where(w => w.CafeId == cafeId).ToResponse().FirstOrDefaultAsync();
return new VersionResponse()
{
Data = cafe
};
}
Moq 依赖于能够创建一个覆盖属性的代理类。
Context.Cafe
不能被覆盖。尝试声明该属性virtual
.
public virtual IDbSet<Cafe> Cafe { get; set; }
尝试在 Set 方法上设置模拟
public virtual DbSet<TEntity> Set<TEntity>() where TEntity : class;
你的情况
var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Set<Cafe>()).Returns(mockSet.Object);
我有一个更好的集成测试方法 您可以对此类测试使用事务回滚 这样你就可以在 Db 中做任何你想做的事,然后 Db 中的每一个动作都回到运行测试之前的最后状态
如果你使用 Ef,你可以将这个类添加到你的测试项目中并享受它
public abstract class PersistTest<T> : IDisposable where T : DbContext, new()
{
protected T DBContext;
private TransactionScope scope;
protected PersistTest()
{
scope = new TransactionScope();
DBContext = new T();
}
public void Dispose()
{
scope.Dispose();
DBContext.Dispose();
}
}
本课程帮助您编写更好的集成测试,
你的测试类应该继承自这个抽象类,并将你的上下文类作为 DbContext 的实例传递
关于您的 Codd 的另一件事是您应该将您的逻辑和数据访问彼此分开
只需定义一个名为 ICofeeRepository 的接口并将您的方法放入其中 你的代码打破了单一职责原则
你的服务应该是这样的
public class CoffeService
{
//inject cafe repository ; _repo
public async Task<VersionResponse> Get(int cafeid)
{
var cafe = _repo.FindById(cafeid);
if (cafe == null)
throw Exception("");
//other wise .....
}
}
你的存储库应该像
public interface ICafeRepository
{
Cafe Get(int cafeid);
}
问题是你在模拟一个你无权访问的具体对象。不要模拟
DbSet
,而是使用接口,以便 Moq 可以在运行时实现它。
而不是这个...
var mockSet = new Mock<DbSet<Cafe>>();
这样做...
var mockSet = new Mock<IDbSet<Cafe>>();