System.NotSupportedException:不支持的表达式:x => x

问题描述 投票:0回答:4

我目前正在尝试最小化我的

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));
}

SUT

public async Task<VersionResponse> Get(int cafeId)
{
    var cafe = await _context.Cafe.Where(w => w.CafeId == cafeId).ToResponse().FirstOrDefaultAsync();
    return new VersionResponse()
    {
        Data = cafe
    };
}
c# unit-testing moq
4个回答
15
投票

Moq 依赖于能够创建一个覆盖属性的代理类。

Context.Cafe
不能被覆盖。尝试声明该属性
virtual
.

public virtual IDbSet<Cafe> Cafe { get; set; }

0
投票

尝试在 Set 方法上设置模拟

public virtual DbSet<TEntity> Set<TEntity>() where TEntity : class;

你的情况

var contextMock = new Mock<Context>();
contextMock.Setup(x => x.Set<Cafe>()).Returns(mockSet.Object);

0
投票

我有一个更好的集成测试方法 您可以对此类测试使用事务回滚 这样你就可以在 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);
}

0
投票

问题是你在模拟一个你无权访问的具体对象。不要模拟

DbSet
,而是使用接口,以便 Moq 可以在运行时实现它。

而不是这个...

var mockSet = new Mock<DbSet<Cafe>>();

这样做...

var mockSet = new Mock<IDbSet<Cafe>>();
© www.soinside.com 2019 - 2024. All rights reserved.