简体   繁体   English

Moq:模拟设置期间奇怪的参数评估

[英]Moq: strange parameter evaluation during mock Setup

This has caused me half a day of debugging and head-scratching.这让我调试了半天,头疼。 I'm using Moq 4.2.1408.0717.我正在使用最小起订量 4.2.1408.0717。 I have a service, which is mocked.我有一项被嘲笑的服务。 And then I have client which is again mocked, but one of the match attributes will use the (mocked) service for its evaluation.然后我有客户端再次被嘲笑,但匹配属性之一将使用(模拟)服务进行评估。 Reduced example below:下面的简化示例:

[TestFixture]
class PPAP
{
    [Test]
    public void Test_Apple_Pen()
    {
        var fruitMock = new Mock<IFruit>(); // the mocked service
        fruitMock.Setup(x => x.GetFruit()).Returns("apple");

        var fw = new FruitWrapper(fruitMock.Object); // object that uses the (mocked) service behind curtains

        var uhhMock = new Mock<IUhh>();
        uhhMock.Setup(x => x.Uhh(fw.GetFruit())).Returns("apple-pen");

        // Utterly unimportant part below, added for clarity:
        var result = uhhMock.Object.Uhh("apple");
        Assert.That(result, Is.EqualTo("apple-pen"));
    }
}

public interface IFruit
{
    string GetFruit();
}

public interface IUhh
{
    string Uhh(string s);
}

public class FruitWrapper
{
    private IFruit _fruit;

    public FruitWrapper(IFruit fruit)
    {
        _fruit = fruit;
    }

    public string GetFruit()
    {
        var f = _fruit.GetFruit(); // f should never be null since I mocked it already!
        // LOOK HERE *jumps and waves* THIS IS THE PROBLEM:
        if (f == null) {
            throw new Exception("POINT OF THE QUESTION HERE: Service mock doesn't function!!!");
        }
        return f; // yet this method will be called twice with f being null before finally called with f == "apple" during Setup()
    }
}

Problem is, the code using the service (FruitWrapper in the example) is not prepared to receive null from the service (that's the point of mocking the service after all!).问题是,使用服务的代码(示例中的FruitWrapper)没有准备好从服务接收空值(毕竟这是模拟服务的重点!)。 Still, during the Setup() of the client mock (uhhMock in the example), FruitWrapper's GetFruit() will be called so that the service will return null (despite the mock already in place).尽管如此,在客户端模拟(示例中的 uhhMock)的 Setup() 期间,将调用 FruitWrapper 的 GetFruit() 以便服务将返回 null(尽管模拟已经到位)。 This was killing my test with an exception.除了一个例外,这杀死了我的测试。 I still have no idea why is this happening.我仍然不知道为什么会这样。 Is this normal behavior or is this a Moq bug?这是正常行为还是 Moq 错误?

(Now that with the reduced example the test doesn't die on null from service, I see that finally the match parameter is evaluated correctly.) (现在使用简化的示例,测试不会因服务为空而死亡,我看到最终匹配参数被正确评估。) Edit: the example was modified to not survive null from service.编辑:该示例已修改为无法从服务中获得空值。 If it did, finally there would be a call where the service returns non-null.如果是这样,最后会有一个服务返回非空值的调用。

(Of course, if I save the parameter (fw.GetFruit()) to a variable before the Setup and use that inside, then everything's fine.) (当然,如果我在 Setup 之前将参数 (fw.GetFruit()) 保存到一个变量中并在内部使用它,那么一切都很好。)

Edit: since people have trouble understanding what I mean.编辑:因为人们难以理解我的意思。 I added some code:我添加了一些代码:

  • a throw in case the service returns null (please, please do manage to understand that THAT is my problem: the service should never return null, it's mocked!抛出,以防服务返回空值(请务必设法理解这是我的问题:服务不应该返回空值,这是被嘲笑的!
  • some unimportant verification at the end to make the test "complete" because the omission seems to irritate people so much that they fail to see anything else.最后一些无关紧要的验证使测试“完成”,因为遗漏似乎会激怒人们,以至于他们看不到其他任何东西。 Note: this is not the point.注意:这不是重点。 Test result is NOT my problem.测试结果不是我的问题。

I'm not sure what your test is supposed to be doing.我不确定你的测试应该做什么。 Is it a complete test?是完整的测试吗?

var uhhMock = new Mock<IUhh>();
        uhhMock.Setup(x => x.Uhh(fw.GetFruit())).Returns("apple-pen");

Change this to use the actual value you want to Setup on:将此更改为使用要设置的实际值:

var uhhMock = new Mock<IUhh>();
            uhhMock.Setup(x => x.Uhh("apple")).Returns("apple-pen");

Another problem is that it appears you're doing some sort of integration test with multiple layers of mocks (using a mock IUhh to get a value from a concrete FruitWrapper which has a mock IFruit ).另一个问题是,您似乎正在对多层模拟进行某种集成测试(使用模拟IUhh从具有模拟IFruit的具体FruitWrapper获取值)。 Try to break it down to using the class under test with mock dependencies.尝试将其分解为使用带有模拟依赖项的测试类。

A good idea is to use the AAA (Arrange, Act, Assert) pattern.一个好主意是使用 AAA(安排、行动、断言)模式。 For example:例如:

[Test]
public void Test_Apple_Pen()
{
    //Arrange
    var fruitMock = new Mock<IFruit>(); 
    fruitMock.Setup(x => x.GetFruit()).Returns("apple");

    var fw = new FruitWrapper(fruitMock.Object);

    //Act
    var result = fw.GetFruit();

    //Assert
    Assert.AreEqual("apple", result);
}

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

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