简体   繁体   English

表达<Func<T, bool> &gt; 在模拟设置中没有按预期工作

[英]Expression<Func<T, bool>> is not working as expected in mock setup

This is the method I want to test:这是我要测试的方法:

public async Task<List<Lesson>> GetProfessionalLessonsByTutorIdAsync(long tutorId)
{
     return await _unitOfWork.Repository<Lesson>().GetEntities(l => l.TeacherId == tutorId && l.LessonTypeId == 1)
                .AsNoTracking().ToListAsync();
}

Here GetEntities method as follows:这里的GetEntities方法如下:

public IQueryable<TEntity> GetEntities(Expression<Func<TEntity, bool>> condition = null,
        Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = null)
{
        IQueryable<TEntity> query = _dbSet;
        if (condition != null)
        {
            query = query.Where(condition);
        }

        if (include != null)
        {
            query = include(query);
        }

        return query;
}

My test method:我的测试方法:

[Fact]
public async Task GetProfessionalLessonsByTutorIdAsync_WithTutorIdInputParam_ReturnsListOfLesson()
    {
        // Arrange
        private readonly Mock<IUnitOfWork> _mockUnitOfWork = new Mock<IUnitOfWork>();
        private readonly Mock<IHttpContextAccessor> _mockContextAccessor = new Mock<IHttpContextAccessor>();
        private readonly Mock<IUserService> _mockUserService = new Mock<IUserService>();


        var fakeLessonList = new List<Lesson>
        {
            new Lesson() { LessonId = 1, LessonTypeId = 1,LanguageId = 1, TeacherId = 1, LessonName = "Professional Lesson"},
            new Lesson() { LessonId = 2,LessonTypeId = 2, LanguageId = 2, TeacherId = 2, LessonName = "Professional Lesson"}
        }.AsQueryable().BuildMock();

        _mockUnitOfWork.Setup(uow => uow.Repository<Lesson>().GetEntities(It.IsAny<Expression<Func<Lesson, bool>>>() ,
            It.IsAny<Func<IQueryable<Lesson>, IIncludableQueryable<Lesson, object>>>())).Returns(fakeLessonList.Object);

        LessonService lessonService = new LessonService(_mockUnitOfWork.Object, _mockContextAccessor.Object, _mockUserService.Object);

        // Act
        var exceptedValue = 1;
        List<Lesson> lessons = await lessonService .GetProfessionalLessonsByTutorIdAsync(1);
        var actualValue = lessons.Count; // Here count should be 1 but its getting 2

        //Assert
        Assert.Equal(exceptedValue, actualValue);
 }

Problem is when await lessonService.GetProfessionalLessonsByTutorIdAsync(1);问题是当await lessonService.GetProfessionalLessonsByTutorIdAsync(1); is being called in the test method its returning 2 items, actually it should return 1 with the matched condition.在测试方法中调用它返回 2 个项目,实际上它应该返回 1 与匹配的条件。

I guess problem is in the following mock set up code:我猜问题出在以下模拟设置代码中:

_mockUnitOfWork.Setup(uow => uow.Repository<Lesson>().GetEntities(It.IsAny<Expression<Func<Lesson, bool>>>() ,
                It.IsAny<Func<IQueryable<Lesson>, IIncludableQueryable<Lesson, object>>>())).Returns(fakeLessonList.Object);

May be I missed something!可能是我错过了什么! Any help from the expert please!请高手帮忙解答!

Note: If I modify the original method as follows then it works.注意:如果我按如下方式修改原始方法,则它可以工作。

public async Task<List<Lesson>> GetProfessionalLessonsByTutorIdAsync(long tutorId)
{
     return await _unitOfWork.Repository<Lesson>().GetEntities().Where(l => l.TeacherId == tutorId && l.LessonTypeId == 1)
                .AsNoTracking().ToListAsync();
}

Now another question is why test method works for GetEntities().Where(l => l.TeacherId == tutorId && l.LessonTypeId == 1) but not works for .GetEntities(l => l.TeacherId == tutorId && l.LessonTypeId == 1) .现在另一个问题是为什么测试方法适用于GetEntities().Where(l => l.TeacherId == tutorId && l.LessonTypeId == 1)但不适用于.GetEntities(l => l.TeacherId == tutorId && l.LessonTypeId == 1)

The problem with your setup is that you set GetEntities to always return the full fakeLessonList list.您设置的问题在于您将GetEntities设置为始终返回完整的fakeLessonList列表。 You never run in against the queries provided as arguments.您永远不会遇到作为参数提供的查询。

To do this, you can use another overload of the Moq Returns() method which provides the arguments passed by the caller via lambda method.为此,您可以使用 Moq Returns()方法的另一个重载,该方法提供调用者通过 lambda 方法传递的参数。 Also, there's no need to mock the list if you want to actually filter out with given conditions, that is, run the queries for real.此外,如果您想根据给定条件实际过滤掉列表,即实际运行查询,则无需模拟列表。

    var fakeLessonList = new List<Lesson>
    {
        new Lesson() { LessonId = 1, LessonTypeId = 1,LanguageId = 1, TeacherId = 1, LessonName = "Professional Lesson"},
        new Lesson() { LessonId = 2,LessonTypeId = 2, LanguageId = 2, TeacherId = 2, LessonName = "Professional Lesson"}
    }.AsQueryable(); // .BuildMock(); - no mock, just a real list

    _mockUnitOfWork.Setup(uow => uow.Repository<Lesson>().GetEntities(It.IsAny<Expression<Func<Lesson, bool>>>(),
        It.IsAny<Func<IQueryable<Lesson>, IIncludableQueryable<Lesson, object>>>()))
            .Returns(
                (Expression<Func<Lesson, bool>> condition,
                 Func<IQueryable<Lesson>, IIncludableQueryable<Lesson, object>> include) =>
                // Run the queries against the list
                // Need to add some checks in case any of those are null
                fakeLessonList.Where(condition)
            );

I didn't test the code, but I hope it provides you the idea of what needs to be adjusted.我没有测试代码,但我希望它能让您了解需要调整的内容。

EDIT: Here's what's happening in your test.编辑:这是您的测试中发生的事情。

  • You set up a mock list, a mock of GetEntities() and some others...您设置了一个模拟列表、一个GetEntities()模拟和其他一些...
  • You call the real await lessonService .GetProfessionalLessonsByTutorIdAsync(1);你调用真正的await lessonService .GetProfessionalLessonsByTutorIdAsync(1); method.方法。
  • Inside the method there is a call to the mock of GetEntities(condition) but it simply ignores the condition because your mock (the way you set it up) doesn't evaluate any conditions, it always returns the full list.在方法内部有一个对GetEntities(condition)模拟的调用,但它只是忽略了condition因为你的模拟(你设置它的方式)不评估任何条件,它总是返回完整列表。

In other words, there's simply no difference whether your GetProfessionalLessonsByTutorIdAsync method calls GetEntities(condition) or GetEntities() because the mock is set up to always return the full list and disregard any condition passed to it.换句话说,无论您的GetProfessionalLessonsByTutorIdAsync方法是调用GetEntities(condition)还是GetEntities()没有区别,因为模拟设置为始终返回完整列表并忽略传递给它的任何条件。

And if you change the code of GetProfessionalLessonsByTutorIdAsync to run GetEntities().Where(condition) you're then finally evaluating the condition against the list that GetEntities() returns.如果您更改GetProfessionalLessonsByTutorIdAsync的代码以运行GetEntities().Where(condition)那么您最终将根据GetEntities()返回的列表评估condition The problem with that is that you no longer have control of what's happening with the condition .这样做的问题是您不再能够控制condition发生的事情。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM