在RhinoMocks 3.6中使用StringDictionary时,为什么会得到“Expected#0,Actual#1”

时间:2012-07-19 04:32:41

标签: c# unit-testing rhino-mocks

今天我在代码中更改为使用StringDictionary而不是Dictionary<string,string>,但旧的单元测试失败了。所以我写了一个小单元测试来测试它。

这是我的小测试:

using Rhino.Mocks;
using NUnit.Framework;
//using CustomDictionary = System.Collections.Specialized.StringDictionary;
using CustomDictionary = System.Collections.Generic.Dictionary<string, string>;

namespace ConsoleApplication1
{
    public interface ITest
    {
        void DoSth(CustomDictionary dic);
    }
    public class OneTest : ITest
    {
        public void DoSth(CustomDictionary dic) {/*do nothing*/}
    }

    [TestFixture]
    public class TestClass
    {
        [Test]
        public void Test1()
        {
            var mockRepository = new MockRepository();
            var test = mockRepository.StrictMock<ITest>();

            using (mockRepository.Record())
            {
                Expect.Call(() => test.DoSth(new CustomDictionary { { "Test", "Test1" } }));
            }
            test.DoSth(new CustomDictionary { { "Test", "Test1" } });

            mockRepository.VerifyAll();
        }
    }
}

如果我使用Dictionary<string,string>,测试将通过,但是当我使用StringDictionary时,测试失败。

这里有什么问题?

3 个答案:

答案 0 :(得分:1)

问题来自StringDictionary实例比较。没有重写的Equals方法,因此实例不是通过内容进行比较,而是通过引用进行比较。如果您将使用相同的实例,则测试将通过:

    [Test]
    public void Test1()
    {
        var mockRepository = new MockRepository();
        var test = mockRepository.StrictMock<ITest>();
        var dictionary = new CustomDictionary { { "Test", "Test1" } };

        using (mockRepository.Record())            
            Expect.Call(() => test.DoSth(dictionary));

        test.DoSth(dictionary);
        mockRepository.VerifyAll();
    }

您可以覆盖Equals课程上的CustomDictionary以进行原始测试通过:

public override bool Equals(object obj)
{
    CustomDictionary other = obj as CustomDictionary;
    if (other == null)
        return false;

    if (Count != other.Count)
        return false;

    foreach (string key in Keys)
    {
        if (!other.ContainsKey(key))
            return false;

        if (this[key] != other[key])
            return false;
    }

    foreach (string key in other.Keys)
    {
        if (!ContainsKey(key))
            return false;
    }

    return true;
}

BTW我希望这不是你真正的考验,因为你在这里测试模拟,而不是测试你的代码。

为什么您的代码与字典一起使用:

据我了解RhinoMocks实现,Rhino.Mocks.Impl.Validate类用于参数验证。您可以查看它的ArgsEqual方法实现:

public static bool ArgsEqual(object[] expectedArgs, object[] actualArgs)
{
    return RecursiveCollectionEqual(expectedArgs, actualArgs);
}

我留给你RecursiveCollectionEqual的详细信息,但有趣的部分是参数比较:

if (current == null)
{
    if (actual != null)
        return false;
}
else if (!SafeEquals(current, actual))
{
    if (current is ICollection)
    {
        if (!RecursiveCollectionEqual((ICollection)current, (ICollection)actual))
            return false;

        continue;
    }
    return false;
}

如您所见,如果参数为ICollection,那么Rhino会更深入地比较预期和实际的集合。 Dictionary实施ICollection,但StringDictionary不实现StringDictionary。因此,Equals类型的参数仅通过引用进行比较。

更新:没有注意到,你有一个别名。只需从类型继承,您就可以覆盖public class CustomDictionary : StringDictionary

{{1}}

答案 1 :(得分:0)

我不知道这是否有帮助,但两者之间的主要区别在于StringDictionairy会自动小写所有键(测试成为测试)。也许这就是测试失败的原因?

答案 2 :(得分:0)

当检查Expect.Call()中传递的参数是否与实际传递的参数匹配时,Rhino Mocks比较按引用。 您的代码中的问题是您创建2个不同对象的自定义词典,一个用于期望,一个用于实际调用,因此它们不匹配:

        using (mockRepository.Record())
        {
            Expect.Call(() => test.DoSth(new CustomDictionary { { "Test", "Test1" } }));
        }
        test.DoSth(new CustomDictionary { { "Test", "Test1" } });

它可以很容易地修复如下(也删除重复):

        var myDictionary = new CustomDictionary { { "Test", "Test1" } }));
        using (mockRepository.Record())
        {
            Expect.Call(() => test.DoSth(myDictionary);
        }
        test.DoSth(myDictionary );