Результаты метода насмешек
-
01-07-2019 - |
Вопрос
Я пытаюсь найти способ подделать результат метода, вызванного из другого метода.
У меня есть метод "loadData", который вызывает отдельный помощник для получения некоторых данных, а затем преобразует их (я заинтересован в тестировании преобразованного результата).
Итак, у меня есть такой код:
public class MyClass(){
public void LoadData(){
SomeProperty = Helper.GetSomeData();
}
public object SomeProperty {get;set;}
}
Я хочу получить известный результат от Помощника.Метод GetSomeData().Могу ли я использовать mocking framework (у меня довольно ограниченный опыт работы с Rhino Mocks, но я открыт для всего), чтобы добиться ожидаемого результата?Если да, то каким образом?
* Редактировать - да, как и ожидалось, я не смог добиться желаемого результата, мне придется придумать лучший способ настройки данных.
Решение
Насколько я знаю, вы должны создать интерфейс или базовый абстрактный класс для Вспомогательного объекта.С помощью Rhino Mocks вы можете затем вернуть нужное значение.
В качестве альтернативы вы можете добавить перегрузку для loadData, которая принимает в качестве параметров данные, которые вы обычно извлекаете из вспомогательного объекта.Это могло бы быть даже проще.
Другие советы
У вас там какая-то проблема.Я не знаю, является ли это упрощенным сценарием вашего кода, но если Вспомогательный класс используется таким образом, то ваш код не поддается тестированию.Во-первых, Вспомогательный класс используется напрямую, так что вы не могу заменить его макетом.Во-вторых, вы вызываете статический метод.Я не знаю насчет C #, но в Java вы не удается переопределить статические методы.
Вам придется выполнить некоторый рефакторинг, чтобы иметь возможность внедрить фиктивный объект с помощью фиктивного метода GetSomeData().
В этой упрощенной версии вашего кода трудно дать вам прямой ответ.У вас есть несколько вариантов:
- Создайте интерфейс для вспомогательного класса и предоставьте клиенту возможность внедрить вспомогательную реализацию в класс MyClass.Но если Helper на самом деле просто служебный класс, то в этом нет особого смысла.
- Создайте защищенный метод в MyClass с именем getSomeData и сделайте так, чтобы он вызывал только Helper.LoadSomeData.Затем замените вызов Helper.LoadSomeData в loadData на для getSomeData.Теперь вы можете имитировать метод getSomeData, чтобы вернуть фиктивное значение.
Остерегайтесь простого создания интерфейса для вспомогательного класса и вводите его с помощью метода.Это может привести к раскрытию деталей реализации.Почему клиент должен предоставить реализацию полезность класс для вызова простой операции?Это увеличит сложность клиентов MyClass.
Я бы порекомендовал преобразовать то, что у вас есть, во что-то вроде этого:
public class MyClass()
{
private IHelper _helper;
public MyClass()
{
//Default constructor normal code would use.
this._helper = new Helper();
}
public MyClass(IHelper helper)
{
if(helper == null)
{
throw new NullException(); //I forget the exact name but you get my drift ;)
}
this._helper = helper;
}
public void LoadData()
{
SomeProperty = this._helper.GetSomeData();
}
public object SomeProperty {get;set;}
}
Теперь ваш класс поддерживает то, что известно как внедрение зависимостей.Это позволяет вам внедрить реализацию вспомогательного класса и гарантирует, что ваш класс должен зависеть только от интерфейса.Когда вы имитируете это, знайте, что вы просто создаете макет, который использует интерфейс IHelper, и передаете его конструктору, и ваш класс будет использовать это, как если бы это был настоящий вспомогательный класс.
Теперь, если вы застряли, используя вспомогательный класс как статический класс, я бы посоветовал вам использовать шаблон прокси / адаптера и обернуть статический класс другим классом, который поддерживает интерфейс IHelper (который вам также нужно будет создать).
Если в какой-то момент вы захотите сделать этот шаг дальше, вы могли бы полностью удалить вспомогательную реализацию по умолчанию из пересмотренного класса и использовать контейнеры IoC (инверсия элемента управления).Однако, если это для вас в новинку, я бы рекомендовал сначала сосредоточиться на основных принципах того, почему все эти дополнительные хлопоты того стоят (это ИМХО).
Ваши модульные тесты будут выглядеть примерно как этот псевдокод:
public Amazing_Mocking_Test()
{
//Mock object setup
MockObject mockery = new MockObject();
IHelper myMock = (IHelper)mockery.createMockObject<IHelper>();
mockery.On(myMock).Expect("GetSomeData").WithNoArguments().Return(Anything);
//The actual test
MyClass testClass = new MyClass(myMock);
testClass.LoadData();
//Ensure the mock had all of it's expectations met.
mockery.VerifyExpectations();
}
Не стесняйтесь комментировать, если у вас есть какие-либо вопросы.(Кстати, я понятия не имею, работает ли весь этот код, я просто ввел его в своем браузере, в основном я иллюстрирую концепции).
Возможно, вы захотите изучить Typemock Isolator, который может "подделывать" вызовы методов, не заставляя вас рефакторинговать ваш код.Я разработчик в этой компании, но решение жизнеспособно, если вы хотите отказаться от изменения своего дизайна (или вынуждены не менять его для удобства тестирования) оно находится на www.Typemock.com
Рой Блог:ISerializable.com
Да, издевательский фреймворк - это именно то, что вы ищете.Вы можете записать / упорядочить, как вы хотите, чтобы возвращались определенные удаленные / заглушенные классы.
Rhino Mocks, Typemock и Moq - все это хорошие варианты для этого.
Сообщение Стивена Уолтера использование Rhino Mocks очень помогло мне, когда я впервые начал играть с Rhino Mocks.
Я бы попробовал что-то вроде этого:
public class MyClass(){
public void LoadData(IHelper helper){
SomeProperty = helper.GetSomeData();
}
Таким образом, вы можете создать макет вспомогательного класса, используя, например, MOQ.