Результаты метода издевательства - PullRequest
9 голосов
/ 18 сентября 2008

Я пытаюсь найти способ подделать результат метода, вызванного из другого метода.

У меня есть метод «LoadData», который вызывает отдельного помощника для получения некоторых данных, а затем преобразует их (мне интересно проверить преобразованный результат).

Итак, у меня есть такой код:

public class MyClass(){
  public void LoadData(){
    SomeProperty = Helper.GetSomeData();
 }
 public object SomeProperty {get;set;}
}

Я хочу получить известный результат из метода Helper.GetSomeData (). Могу ли я использовать фальшивый фреймворк (у меня довольно ограниченный опыт работы с Rhino Mocks, но я готов ко всему), чтобы добиться ожидаемого результата? Если да, то как?

* Редактировать - да, как и ожидалось, я не смог добиться желаемого взлома, мне придется найти лучший способ настроить данные.

Ответы [ 6 ]

10 голосов
/ 18 сентября 2008

У вас есть проблема там. Я не знаю, является ли это упрощенным сценарием вашего кода, но если класс Helper используется таким образом, то ваш код не тестируется. Во-первых, класс Helper используется напрямую, поэтому вы не можете заменить его на макет . Во-вторых, вы вызываете статический метод. Я не знаю о C #, но в Java вы не можете переопределить статические методы .

Вам потребуется выполнить некоторый рефакторинг, чтобы иметь возможность внедрить фиктивный объект с помощью фиктивного метода GetSomeData ().

В этой упрощенной версии вашего кода сложно дать вам прямой ответ. У вас есть несколько вариантов:

  • Создайте интерфейс для класса Helper и предоставьте клиенту способ внедрить реализацию Helper в класс MyClass. Но если Helper - это действительно служебный класс, это не имеет особого смысла.
  • Создайте защищенный метод в MyClass с именем getSomeData и сделайте так, чтобы он вызывал только Helper.LoadSomeData. Затем замените вызов Helper.LoadSomeData в LoadData на getSomeData. Теперь вы можете смоделировать метод getSomeData для возврата фиктивного значения.

Остерегайтесь простого создания интерфейса к классу Helper и добавьте его с помощью метода. Это может раскрыть детали реализации. Почему клиент должен предоставить реализацию служебного класса для вызова простой операции? Это увеличит сложность клиентов MyClass.

8 голосов
/ 18 сентября 2008

Я бы порекомендовал преобразовать то, что у вас есть, во что-то вроде этого:

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, и передаете его конструктору, и ваш класс будет использовать его, как будто это настоящий класс Helper.

Теперь, если вы застряли, используя класс Helper в качестве статического класса, я бы посоветовал вам использовать шаблон прокси / адаптера и обернуть статический класс другим классом, поддерживающим интерфейс IHelper (который вам также потребуется создать ).

Если в какой-то момент вы захотите сделать этот шаг дальше, вы можете полностью удалить стандартную реализацию Helper из пересмотренного класса и использовать контейнеры IoC (Inversion of Control). Если это новость для вас, я бы рекомендовал сначала сосредоточиться на основах того, почему все эти дополнительные хлопоты стоят того времени (это ИМХО).

Ваши юнит-тесты будут выглядеть примерно так: псевдо-код:

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();
}

Не стесняйтесь комментировать, если у вас есть какие-либо вопросы. (Кстати, я понятия не имею, работает ли весь этот код, я просто набрал его в своем браузере, я в основном иллюстрирую концепции).

2 голосов
/ 19 сентября 2008

Возможно, вы захотите взглянуть на Typemock Isolator, который может «подделывать» вызовы методов, не заставляя вас реорганизовывать ваш код. Я являюсь разработчиком в этой компании, но решение будет жизнеспособным, если вы захотите не менять свой дизайн (или вынуждены не менять его для тестирования) это на www.Typemock.com

Рой блог: ISerializable.com

2 голосов
/ 18 сентября 2008

Насколько я знаю, вы должны создать интерфейс или базовый абстрактный класс для объекта Helper. С помощью Rhino Mocks вы можете вернуть желаемое значение.

Кроме того, вы можете добавить перегрузку для LoadData, которая принимает в качестве параметров данные, которые вы обычно получаете из объекта Helper. Это может быть даже проще.

0 голосов
/ 18 сентября 2008

Я бы попробовал что-то вроде этого:

public class MyClass(){
  public void LoadData(IHelper helper){
    SomeProperty = helper.GetSomeData();
 }

Таким образом, вы можете смоделировать вспомогательный класс, используя, например, MOQ.

0 голосов
/ 18 сентября 2008

Да, насмешливая структура - это именно то, что вы ищете. Вы можете записать / организовать, как вы хотите, чтобы возвращались определенные отключенные / заглушенные классы.

Rhino Mocks, Typemock и Moq - хорошие варианты для этого.

Пост Стивена Уолтера об использовании Rhino Mocks мне очень помог, когда я впервые начал играть с Rhino Mocks.

...