Зачем получать "Ожидаемый #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). Может быть, поэтому тест не проходит?