Moq链接表达式导致参数计数不匹配。

3
我正在尝试使用Moq模拟存储库。我发现了许多类似问题的提问,但没有一个能够解决我遇到的问题。
因此,我使用可从这里下载的存储库。更具体地说,可以在这里查看存储库本身,并且我正在使用的查询扩展可以在这里看到。
这是我的测试设置:
        // A List<> of fakes.
        this.containers = Builder<Container>.CreateListOfSize(10).Build();

        // Here I'm trying to set up the mock object for the query.
        this.containerRepoMock.Setup(p => p.
            Query(It.IsAny<Expression<Func<Container, bool>>>())
                .Include(It.IsAny<Expression<Func<Container, object>>>())
                .Select())
            .Returns((Expression<Func<Container, bool>> query, Expression<Func<Container, object>> include) => 
                 this.containers.AsQueryable().Where(query).Include(include));

        // Tell the service to use the mock repository.
        this.containerService = new ContainerService(mockUnitOfWork.Object);

这是我正在尝试测试的服务方法:

    public ContainerDto GetContainerAndItsCategories(int containerId)
    {
        var entity = Repository
            .Query(w => w.ContainerId == containerId)
            .Include(c => c.Categories)
            .Select();

        var output = Mapper.EntityToDto(entity.SingleOrDefault());

        return output;
    }

每当我尝试在使用模拟存储库的测试中运行此服务方法时,它都会抛出一个异常"System.Reflection.TargetParameterCountException: Parameter count mismatch."。
我尝试在模拟设置的.返回()方法中添加额外的对象参数,但没有成功。它总是抛出相同的异常。从我阅读的内容来看,Moq在测试表达式方面有些限制,但我已经看到人们使用类似的方法取得了成功。
由于Include()和Query() 方法返回的是IQueryFluent() 实例而不是直接表达式,因此我尝试在Moq return()方法中使用QueryFluent()类,但无法成功使用queryable(编译错误)。
非常感谢任何帮助,我真的想能够使用单元测试正确地测试这个。
编辑 - 我查看过的类似问题:

虽然你的问题写得很好,但我需要花费大量精力才能将其简化为一个问题陈述。下次尝试创建一个更好的SSCCE,以便更快地获得答案。 - Patrick Quirk
我试图让它简单易懂,但也在努力包含可能相关的信息。猜想我做得过多了。感谢您花时间查看。我会在今天晚些时候尝试您的解决方案。 - grimurd
没问题,它完成了任务。祝你好运! - Patrick Quirk
1个回答

2
这个问题出在这一行代码上:
this.containerRepoMock.Setup(p => p.
        Query(It.IsAny<Expression<Func<Container, bool>>>())
        .Include(It.IsAny<Expression<Func<Container, object>>>())
        .Select())

据我所知,你无法在一次Setup调用中设置一组方法。此外,在这行代码中:

.Returns((Expression<Func<Container, bool>> query, Expression<Func<Container, object>> include) => 
    this.containers.AsQueryable().Where(query).Include(include));

你要告诉Moq期望(在Setup调用中)调用一个具有两个参数(queryinclude)的单个方法,并将它们传递到此处的Returns lambda。显然不是这种情况:你有两个一参数方法的调用,因此Moq会抛出参数不匹配异常。
你需要将其分解为组件并设置模拟返回值。此外,由于你想利用Expression对象,所以你需要保存它们以备后用。例如:
var containerRepoMock = new Mock<IRepositoryAsync<Container>>();
var queryFluentQueryMock = new Mock<IQueryFluent<Container>>();
var queryFluentIncludeMock = new Mock<IQueryFluent<Container>>();

Expression<Func<Container, bool>> query = null;
containerRepoMock.Setup(p => p.Query(It.IsAny<Expression<Func<Container, bool>>>()))
                 .Callback<Expression<Func<Container, bool>>>(q => query = q)
                 .Returns(queryFluentQueryMock.Object);

Expression<Func<Container, object>> include = null;
queryFluentQueryMock.Setup(p => p.Include(It.IsAny<Expression<Func<Container, object>>>()))
                    .Callback<Expression<Func<Container, object>>>(i => include = i)
                    .Returns(queryFluentIncludeMock.Object);

queryFluentIncludeMock.Setup(p => p.Select())
                 .Returns(containers.AsQueryable().Where(query).Include(include));

请原谅任何编译错误,我没有下载您提供的库尝试此操作。

测试可以运行,但是我必须在定义表达式时设置它们(否则会出现编译器错误“访问之前本地变量可能未初始化”)。因此,containerRepoMock和queryFluentQueryMock上的Setup()似乎没有正确运行。至少它们没有替换预设的表达式。有什么想法可能缺失了吗? - grimurd
从我目前的了解来看,我不知道为什么它们不能工作。不幸的是,我几周内没有调试的机会。如果你找出问题,请回来留言! - Patrick Quirk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接