简体   繁体   English

当对象是方法中的参数时,如何正确使用Moq对象

[英]How to use Moq object correctly when object is parameter in method

I am not completely new to unit testing, however I am new to the Moq library and I have run into a problem. 我对单元测试并不完全陌生,但是对于Moq库我并不陌生,并且遇到了问题。 I am confused on why my unit test fails. 我对为什么我的单元测试失败感到困惑。 Here is the unit test I am trying to write. 这是我要编写的单元测试。

[TestInitialize]
    public void SetUp()
    {
        //...
        optionsMock = new Mock<IDictionary<string, string>>();
        //...
    }

[TestMethod]
    public void TestFunction()
    {
        // Arrange
        //var options = new Dictionary<string, string>() { { keyValue, true.ToString() } }; // only way to get the unit test to pass right now
        optionsMock.Setup(a => a.ContainsKey(It.Is<string>(b => b == keyValue))).Returns(false);
        optionsMock.Setup(c => c.Add(It.Is<string>(d => d == keyValue), It.Is<string>(e => e == true.ToString()))).Verifiable();
        optionsMock.Setup(f => f.ContainsKey(It.Is<string>(g => g == keyValue))).Returns(true);
        optionsMock.Setup(h => h[It.Is<string>(i => i == keyValue)]).Returns(true.ToString()); 

        // Act
        int projectId = sut.Open(stringValue, booleanValue, stringValue, stringValue, IDictionary<string, string>, out errorString);

        // Assert
        optionsMock.Verify(a => a.ContainsKey(It.Is<string>(b => b == keyValue)), Times.Once());
        optionsMock.Verify(c => c.Add(It.Is<string>(d => d == keyValue), It.Is<string>(e => e == true.ToString())), Times.Once());
        optionsMock.Verify(f => f.ContainsKey(It.Is<string>(g => g == keyValue)), Times.Once());
        optionsMock.Verify(h => h[It.Is<string>(i => i == keyValue)], Times.Once()); // This fails 
        Assert.AreNotEqual(0, id); // This fails even if I remove the line above
    }

The line commented out is the only way I can get the test to pass, but I feel I should be able to use a mock dictionary as well. 注释掉的行是我可以通过测试的唯一方法,但是我觉得我也应该可以使用模拟字典。 The problem I am experiencing is that eventually sut.Open(...) makes a call to an internal class that has an IDictionary as a parameter. 我遇到的问题是,最终sut.Open(...)对具有IDictionary作为参数的内部类进行了调用。 In that method there is a check to see if the dictionary is null. 在该方法中,将检查字典是否为空。 Whenever I run the unit test without a real dictionary, the null check always evaluates to true and I get a false fail. 每当我在没有真正词典的情况下运行单元测试时,null检查总会得出true,而我会得到错误的失败。 However if the real dictionary is passed in, I get a passing test. 但是,如果通过了真正的词典,我会通过测试。 Here is an example of the internal code. 这是内部代码的示例。

public int Open(..., IDictionary<string, string> dictionary, ...)
    {
        //...
        if(!dictionary.ContainsKey(key))
        {
           dictionary.Add(key, true);
        }

        InternalClass.Method(dictionary);
        //...
    }

Here is the internal classes method 这是内部类方法

public void Method(IDictionary<string, string> dictionary)
    {
       if(dictionary != null && dictionary.ContainsKey(key))
       {
          string value = dictionary[key];
          //... Do something
       }
       else
       {
          //... Do something else
       }
    }

It always executes Do something else, is there away around this? 它总是执行其他操作,这周围还存在吗?

Look closer to the following line: 仔细查看以下行:

optionsMock.Setup(a => a.ContainsKey(It.Is<string>(b => b == keyValue))).Returns(false);

This means ContainsKey should return false . 这意味着ContainsKey应该返回false So the condition: 因此条件:

if (dictionary != null && dictionary.ContainsKey(key))

can never be satisfied. 永远无法满足。

I guess you meant to write .Returns(true); 我猜你打算写.Returns(true);

Alright so I think this is the answer I am happy with. 好吧,我认为这是我很满意的答案。 I found some other places online where this was used by other people as well. 我在网上找到了其他人也使用过的其他地方。 Here is the Setup and Verify section now 现在是“设置和验证”部分

bool firstCall = true;

        optionsMock.Setup(a => a.ContainsKey(It.Is<string>(b => b == keyValue))).Returns(() =>
        {
            if (firstCall)
            {
                firstCall = false;
                return false;
            }

            return true;
        });

Then I verify below with the following: 然后,我通过以下内容进行验证:

optionsMock.Verify(a => a.ContainsKey(It.Is<string>(b => b == fullPath)), Times.Exactly(2));

The only thing I don't like about this, is the added logic in the test :(. However I don't see any other way around it. Thanks for all the help! 关于此,我唯一不喜欢的是在测试中添加了逻辑:(。但是我看不到其他方法。感谢所有帮助!

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

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