【问题标题】:How to Moq the "Expression<Func<T, TType>>"如何起订“表达式<Func<T, Type>>”
【发布时间】:2021-12-13 13:34:49
【问题描述】:

我正在尝试测试存储库的 Get 方法。签名如下:

public virtual async Task<IList<TType>> GetAsync<TType>(
            Expression<Func<T, bool>> filter,
            Expression<Func<T, TType>> select = null,
            Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
            params Expression<Func<T, object>>[] includes)
{
......................
}

该方法通常以如下方式调用:

var user = await repository.GetAsync(x => x.Name == name, x => new { x.Dob, x.Age });

它适用于真实数据,当我尝试设置此方法以返回具有问题开始的特定 lambda 的特定“用户”时。

我的问题是如何模拟下面的选择器并指定 Returns 语句。

Expression<Func<T, TType>> select = null,

我已经尝试了以下方法并最终出现错误。

 unitOfWork.Setup(_ => _.repository.GetAsync(
                It.IsAny<Expression<Func<User, bool>>>(),
                It.IsAny<Expression<Func<User, object>>>()))
                .Returns(Task.FromResult(users));

错误: 无法从 'System.Threading.Tasks.Task' 转换为 'System.Threading.Tasks.Task'

谁能帮助我,或者至少把我推向正确的方向?

【问题讨论】:

  • 你不能模拟一个表达式。就像名字已经说的那样,它是一种表达方式。它将被存储库读取,因此您必须模拟存储库 .GetAsync() 并确保它可以处理所需的有效表达式并在测试中返回伪造的结果。
  • 嗨奥利弗,感谢您的回复。是的,我的问题是在 .GetAsync() 如何模拟表达式。
  • 你试过.ReturnsAsync(users)吗?

标签: linq unit-testing asp.net-core moq repository-pattern


【解决方案1】:

Seems to work 符合预期:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Moq;
using Shouldly;

public class User
{
    public int Id { get; set; }
}

public interface IMyRepository<T>
{
    Task<IList<TType>> GetAsync<TType>(Expression<Func<T, bool>> filter, Expression<Func<T, TType>> select = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, params Expression<Func<T, object>>[] includes);
}

public class Program
{
    public static async Task Main()
    {
        var users = new List<int>{42};
        var repository = new Mock<IMyRepository<User>>();
        repository.Setup(r => r.GetAsync(It.IsAny<Expression<Func<User, bool>>>(), It.IsAny<Expression<Func<User, int>>>(), It.IsAny<Func<IQueryable<User>, IOrderedQueryable<User>>>())).ReturnsAsync(users);
        var result = await repository.Object.GetAsync(user => true, user => user.Id, query => query.OrderBy(u => u.Id));
        result.ShouldBe(users);
    }
}

结果类型取决于您的选择参数。如果你改变它的返回类型,你就会改变结果:

var users = new List<User> { new User { Id = 42 } };
var repository = new Mock<IMyRepository<User>>();
repository.Setup(r => r.GetAsync(
    It.IsAny<Expression<Func<User, bool>>>(),
    It.IsAny<Expression<Func<User, User>>>(), // Here you define the outcoming type
    It.IsAny<Func<IQueryable<User>, IOrderedQueryable<User>>>()))
    .ReturnsAsync(users);

var result = await repository.Object.GetAsync(
    user => true,
    user => user, // Here you define the outcoming type
    query => query.OrderBy(u => u.Id));

result.ShouldBe(users);

【讨论】:

  • 嗨 Oliver,感谢您的回复,还有一个问题,而不是 int 如何将 POCO 类作为返回类型传递(例如:具有名称和 id 的用户模式)。
  • 嗨奥利弗,感谢您的快速回复,是的,它工作正常,但在一种情况下,当我只获得所需的列时,模拟返回空值,而不是获取整个 POCO 类。例如: var result = await repository.Object.GetAsync( user => true, user => x => new { user.Name }, query => query.OrderBy(u => u.Id));如何模拟这个用户 => x => new { user.Name }。提前致谢。
  • 您的 Mock.Setup 方法必须返回所需的列表类型。表达式本身将被忽略。检查我的第一个示例或这种结果的小提琴。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-22
  • 2013-05-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多