Зачем получать "Ожидаемый #0, Фактический #1" при использовании StringDictionary в RhinoMocks 3.6

Сегодня я перешел на использование 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 ответа

Решение

Проблема идет от 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;
}

Кстати, я надеюсь, что это не ваш настоящий тест, потому что здесь вы тестируете макет, а не тестируете свой код.

ПОЧЕМУ ВАШ КОД работает со СЛОВАРЬМ:

Как я понимаю реализацию 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

Дело в том, что Rhino Mocks сравнивает по ссылке при проверке, соответствуют ли параметры, переданные в Expect.Call(), параметрам, фактически переданным.
Проблема в вашем коде состоит в том, что вы создаете 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 );

Я не знаю, поможет ли это, но главное различие между ними заключается в том, что StringDictionairy автоматически выполняет строчные буквы всех ваших ключей (Test становится test). Может быть, поэтому тест не проходит?

Другие вопросы по тегам