简体   繁体   English

Moq:设置一个比较Lambda表达式内的引导的方法失败

[英]Moq: Setting Up Method That Compares Guids Inside A Lambda Expression Fails

This seems like a very simple task but I find it extremely hard to accomplish in Moq. 这似乎是一个非常简单的任务,但是我发现在Moq中很难完成。 I have a repository that calls a unit of work to query a random picture from a database. 我有一个存储库,该存储库调用工作单元从数据库中查询随机图片。 This query has one constraint; 这个查询有一个约束; the random picture from the database cannot be equal to the current picture being displayed. 来自数据库的随机图片不能等于正在显示的当前图片。 I'm building an NUnit test for the repository and I'd like to mock the unit of work like so: 我正在为存储库构建一个NUnit测试,我想像这样模拟工作单元:

[TestFixture]
public class When_the_next_random_picture_for_TopSlidePicture_show_has_been_requested
{
    private Guid _currentPictureID;
    private Picture _randomPicture;
    private Mock<IUnitOfWork<Guid>> _unitOfWorkMock;

    [SetUp]
    public void Context()
    {
        _currentPictureID = Guid.NewGuid();
        _randomPicture = new Picture { ID = Guid.NewGuid() };
        _unitOfWorkMock = new Mock<IUnitOfWork<Guid>>();
        //TODO:  Find out how to setup and verify expression when mocking method with equality comparison in a lambda expression.
        _unitOfWorkMock.Setup(uow => uow.GetRandom<Picture>(pic => pic.ID != _currentPictureID))
            .Returns(_randomPicture);
    }

    [Test]
    public void The_PictureRepository_can_query_next_random_picture()
    {
        //Arrange
        var picRepo = new PictureRepository(_unitOfWorkMock.Object);

        //Act
        var randomPicture = picRepo.GetNextRandomPicture(_currentPictureID);

        //Assert
        _unitOfWorkMock.Verify(uow => 
            uow.GetRandom<Picture>(pic => pic.ID != _currentPictureID)
            , Times.Once());

        Assert.AreEqual(_randomPicture, randomPicture);
    }
}

In the code above, the GetRandom<Picture>(Expression<Func<Picture, bool>>) in the UnitOfWork is supposed to return any picture in the database who's Guid ID isn't equal to the current pics ID. 在上面的代码中,应该假定UnitOfWork中的GetRandom<Picture>(Expression<Func<Picture, bool>>)返回数据库中Guid ID不等于当前图片ID的任何图片。 However, Setup() method called from _unitOfWorkMock returns null regardless of _randomPicture's value. 但是,不管_randomPicture的值如何,从_unitOfWorkMock调用的Setup()方法都将返回null。 After much research I found the following procedure: 经过大量研究,我发现以下过程:

_unitOfWorkMock = new Mock<IUnitOfWork<Guid>>();
_unitOfWorkMock.Setup(uow => uow.GetRandom<Picture>(It.IsAny<Expression<Func<Picture, bool>>>()))
    .Returns(_randomPicture); 

var randomPicture = picRepo.GetNextRandomPicture(_currentPictureID);

//Assert
_unitOfWorkMock.Verify(uow => 
    uow.GetRandom<Picture>(It.IsAny<Expression<Func<Picture, bool>>>())
    , Times.Once());

This allows the test to pass. 这样可以使测试通过。 BUT, I didn't test whether the picture returned from the db has the same Guid ID of the current picture ID passed in; 但是,我没有测试从数据库返回的图片是否具有与传入的当前图片ID相同的Guid ID。 which is the key principle for even building the test!!! 这是进行测试的关键原则!!!

I love the Moq framework and prefer it over all other testing tools, but this seems like a massive breach of integrity as far as a unit testing platform goes. 我喜欢Moq框架,并且比其他所有测试工具都更喜欢它,但是就单元测试平台而言,这似乎严重违反了完整性。 Please, someone, inform me on what I'm not seeing clearly and show me a simple and easy way to accomplish this seemingly simple task! 请有人告诉我我不清楚的地方,并告诉我一种简单易行的方法来完成此看似简单的任务!

Gratitude 感谢

UPDATE UPDATE

Thanks to @StriplingWarrior, I was able to solve the problem! 感谢@StriplingWarrior,我得以解决问题! His boolean algebra was wrong, but his answer was sound ;-) I found these modifications to my above code to work: 他的布尔代数是错误的,但是他的答案很合理;-)我发现对上面的代码进行了以下修改:

        _unitOfWorkMock = new Mock<IUnitOfWork<Guid>>();
        _unitOfWorkMock.Setup(uow => uow.GetRandom<Picture>(It.Is<Expression<Func<Picture, bool>>>(e => TestIdMatchExpression(e))))
            .Returns(_randomPicture);

    private bool TestIdMatchExpression(Expression<Func<Picture, bool>> expr)
    {
        var func = expr.Compile();

        Assert.IsFalse(func(new Picture { ID = _referencePictureID }));

        return true;
    }

         //Assert
         _unitOfWorkMock.Verify(uow =>
             uow.GetRandom<Picture>(It.Is<Expression<Func<Picture, bool>>>(e => TestIdMatchExpression(e)))
             , Times.Once());

Thanks again @StriplingWarrior! 再次感谢@StriplingWarrior!

You should be able to use a special overload of It.Is<>() to test the expression: 您应该能够使用It.Is<>()的特殊重载来测试表达式:

It.Is<Expression<Func<Picture, bool>>>(e => TestIdMatchExpression(e))

From there you just have to figure out the best way to check the expression that gets passed in. The easiest way is probably to test the behavior: the expression should match a picture with the same guid, and reject a picture with a different one. 从那里,您只需要找出检查传入的表达式的最佳方法。最简单的方法可能是测试行为:表达式应匹配具有相同引导的图片,并拒绝具有不同引导的图片。

bool TestIdMatchExpression(Expression<Func<Picture, bool>> expr)
{
    var func = expr.Compile();
    Assert.IsTrue(func(new Picture{ID = _currentPictureID}));
    Assert.IsFalse(func(new Picture{ID = Guid.NewGuid()}));
}

Remember, when you do unit testing of a method, you have to isolate that test, mock every external dependency including database in your case , do the test of that method for different conditions of the external dependencies 请记住,在对方法进行单元测试时,必须隔离该测试,模拟每个外部依赖关系(包括您所用的数据库),并针对外部依赖关系的不同条件对该方法进行测试

 Picture randomPictureExpected = new Picture{ ID=Guid.NewGuid()}; 
   _unitOfWorkMock.Setup(uow => uow.GetRandom<Picture>(pic=>pic.ID!=_currentPictureID))
                   .Returns(randomPictureExpected);   

and your assert would be like 而你的断言就像

var randomPictureActual = picRepo.GetNextRandomPicture(_currentPictureID);

  Assert.AreEqual (randomPictureExpected.ID, randomPictureActual.ID);

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

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