Зачем создавать фиктивные объекты? - PullRequest
25 голосов
/ 12 сентября 2009

Во время недавнего интервью меня спросили, почему можно создавать фиктивные объекты. Мой ответ звучал примерно так: «Взять базу данных - если вы пишете тестовый код, вы можете не захотеть, чтобы этот тест был подключен к производственной базе данных, где будут выполняться реальные операции».

Судя по ответу, мой ответ явно не был тем, что искал интервьюер. Какой ответ лучше?

Ответы [ 7 ]

30 голосов
/ 12 сентября 2009

Я бы подвел итог так:

  • Изоляция - Вы можете протестировать только метод, независимо от того, что он вызывает. Ваш тест становится реальным модульным тестом (самое важное ИМХО)
  • Сокращение времени разработки теста - обычно проще использовать макет, чем создавать целый класс только для того, чтобы помочь вам протестировать
  • Это позволяет вам тестировать, даже если вы не реализовали все зависимости - Вам даже не нужно создавать, например, свой класс репозитория, и вы сможете протестировать класс что бы использовать этот репозиторий
  • Держит вас подальше от внешних ресурсов - помогает в том смысле, что вам не требуется доступ к базам данных, или для вызова веб-служб, или для чтения файлов, или для отправки электронных писем, или для снятия кредита карточка и тд ...

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

5 голосов
/ 12 сентября 2009

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

Рассмотрим нисходящий подход: сначала вы разрабатываете GUI, но в GUI должны быть данные. Таким образом, вы создаете фиктивную базу данных, которая просто возвращает std :: vector <> данных. Вы определили «контракт» отношений. Кому интересно, что происходит внутри объекта базы данных - до тех пор, пока мой список GUI получит std :: vector <> Я счастлив. Это может предоставить ложную информацию для входа пользователя, что бы вам ни понадобилось для работы графического интерфейса.

Рассмотрим восходящий подход. Вы написали парсер, который читает в текстовых файлах с разделителями. Как узнать, работает ли он? Вы пишете фиктивный «приемник данных» для этих объектов и направляете туда данные, чтобы проверить (хотя обычно), что данные прочитаны правильно. Для модуля на следующем уровне может потребоваться 2 источника данных, но вы написали только один.

И при определении фиктивных объектов вы также должны определить договор о том, как связаны отношения. Это часто используется в программировании на основе тестов. Вы пишете тестовые случаи, используете фиктивные объекты, чтобы заставить его работать, и часто интерфейс фиктивного объекта становится конечным интерфейсом (поэтому в какой-то момент вы можете захотеть разделить интерфейс фиктивного объекта на чистый абстрактный класс) .

Надеюсь, это поможет

5 голосов
/ 12 сентября 2009

При модульном тестировании каждый тест предназначен для тестирования одного объекта. Однако большинство объектов в системе будут иметь другие объекты, с которыми они взаимодействуют. Ложные объекты - это фиктивные реализации этих других объектов, используемые для изоляции тестируемого объекта.

Преимущество этого состоит в том, что любые неудачные модульные тесты обычно изолируют проблему для тестируемого объекта. В некоторых случаях проблема будет связана с фиктивным объектом, но эти проблемы проще выявить и исправить.

Возможно, было бы неплохо написать несколько простых модульных тестов для фиктивных объектов.

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

Другое использование может заключаться в издевательстве над пользовательским интерфейсом при тестировании объекта контроллера в шаблоне MVC. Это позволяет лучше автоматизировать тестирование компонентов пользовательского интерфейса, которые могут имитировать взаимодействие с пользователем.

Пример:

public interface IPersonDAO
{
    Person FindById(int id);
    int Count();
}

public class MockPersonDAO : IPersonDAO
{
    // public so the set of people can be loaded by the unit test
    public Dictionary<int, Person> _DataStore;

    public MockPersonDAO()
    {
        _DataStore = new Dictionary<int, Person>();
    }

    public Person FindById(int id)
    {
        return _DataStore[id];
    }

    public int Count()
    {
        return _DataStore.Count;
    }
}
3 голосов
/ 12 сентября 2009

Вот несколько ситуаций, когда насмешка необходима e:

  1. При тестировании взаимодействия с графическим интерфейсом
  2. При тестировании веб-приложения
  3. При тестировании кода, взаимодействующего с оборудованием
  4. Когда вы тестируете устаревшие приложения
3 голосов
/ 12 сентября 2009

Имитация объектов / функций также может быть полезна при работе в команде. Если вы работаете с частью базы кода, которая зависит от другой части базы кода, за которую отвечает кто-то еще - которая все еще пишется или еще не написана - имитируемый объект / функция полезен в давая вам ожидаемый результат, чтобы вы могли продолжать работу, не задерживаясь в ожидании, пока другой человек закончит свою работу.

2 голосов
/ 13 сентября 2009

Я пойду в другом направлении здесь. Stubbing / Faking делает все, что упомянуто выше, но, возможно, интервьюеры думали о ложных объектах как о поддельном объекте, который заставляет тест пройти или не пройти Я основываюсь на терминологии xUnit . Это могло бы привести к некоторому обсуждению проверки состояния стихов поведения / взаимодействия .

Ответ, который они, возможно, искали: что фиктивный объект отличается от заглушки. Заглушка эмулирует зависимость для тестируемого метода. Заглушка не должна вызывать сбой теста. Это делает макет , а также проверяет, как и когда он вызывается. Мокость заставляет тест пройти или не пройти в зависимости от базового поведения. Преимущество этого заключается в меньшей зависимости от данных во время теста, но более тесной связи с реализацией метода.

Конечно, это предположение, более вероятно, что они просто хотели, чтобы вы описали преимущества заглушки и DI.

0 голосов
/ 14 сентября 2009

Чтобы использовать немного другой подход (как я думаю, что макеты были хорошо описаны выше):
«Возьмите базу данных - если вы пишете тестовый код, вы можете не захотеть, чтобы этот тест был подключен к производственной базе данных, где будут выполняться реальные операции.»

IMO плохой способ указать пример использования. Вы никогда бы не «подключили его к базе данных prod» во время тестирования с или без макетов. У каждого разработчика должна быть локальная база данных для разработчиков. И тогда вы перейдете на базу данных тестовых сред, затем, возможно, UAT и, наконец, Prod. Вы не издеваетесь над тем, чтобы избежать использования действующей базы данных, вы издеваетесь над тем, чтобы классы, которые не зависят напрямую от базы данных, не требовали от вас установки базы данных.

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

...