Какова цель макет объектов? - PullRequest
152 голосов
/ 02 сентября 2010

Я новичок в модульном тестировании, и я постоянно слышу, как часто встречаются слова «фиктивные объекты».С точки зрения непрофессионала, может ли кто-нибудь объяснить, что такое фиктивные объекты и для чего они обычно используются при написании модульных тестов?

Ответы [ 10 ]

327 голосов
/ 02 сентября 2010

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

Модульное тестирование

Представьте себе модульное тестирование для этогоsystem:

cook <- waiter <- customer

Как правило, легко представить тестирование низкоуровневого компонента, такого как cook:

cook <- test driver

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

Сложнее проверить средний компонент, например официанта, который использует поведение других компонентов.Наивный тестировщик может проверить компонент официанта так же, как мы тестировали компонент повара:

cook <- waiter <- test driver

Тестировщик закажет разные блюда и обеспечит, чтобы официант вернул правильное блюдо.К сожалению, это означает, что этот тест компонента официанта может зависеть от правильного поведения компонента приготовления.Эта зависимость еще хуже, если у компонента cook есть какие-либо недружественные к тесту характеристики, такие как недетерминированное поведение (меню включает сюрприз шеф-повара как блюдо), множество зависимостей (cook не готовит без всего своего персонала) или многоресурсы (для некоторых блюд требуются дорогостоящие ингредиенты или для их приготовления требуется час).

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

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

    -----------------------
   |                       |
   v                       |
test cook <- waiter <- test driver

Здесь тест-повар "в сговоре" с тест-драйвером.В идеале тестируемая система спроектирована таким образом, чтобы тестируемый повар мог легко заменить ( впрыскивается ) на работу с официантом без изменения производственного кода (например, без изменения кода официанта).

Mock Objects

Теперь тестовый повар (тестовый дубль) может быть реализован различными способами:

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

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

    -----------------------
   |                       |
   v                       |
mock cook <- waiter <- test driver

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

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

Разговор о фиктивном тесте может выглядеть следующим образом:

тест-водитель до фиктивный повар : ожидать заказа хот-догаи дайте ему этот фиктивный хот-дог в ответ

тест-водитель (выдавая себя за клиента) на официант : Я хотел бы хот-дог, пожалуйста
официант до ложный повар : 1 хот-дог, пожалуйста
ложный повар до официант : оформление заказа: 1 хот-дог готов (дает фиктивного хот-дога официанту)
официант до тест-водитель : вот ваш хот-дог (дает фиктивного хот-дога тест-драйверу)

тестовый водитель : ИСПЫТАНИЕ УСПЕШНО!

Но так как наш официант новичок, вот что может произойти:

тест-водитель до фальшивый повар : ожидайте приказ хот-дога и дайте ему этот фиктивный хот-дог в ответ

тест-водитель (выдавая себя за клиента) до официант : Хотелось бы хот-дог, пожалуйста
официант до ложный повар : 1 гамбургер, пожалуйста
Ложный повар останавливает тест: Мне сказали ожидать заказа хот-дога!

тестовый драйвер отмечает проблему: ТЕСТ НЕ ПРОВЕРЕН! - официант изменил порядок

или

тест-водитель до фальшивый повар : ожидайте приказ хот-дога и дайте ему этот фиктивный хот-дог в ответ

тест-водитель (выдавая себя за клиента) до официант : Хотелось бы хот-дог, пожалуйста
Официант до Ложный повар : 1 хот-дог, пожалуйста
Ложный повар до Официант : Заказ: 1 хот-дог готов (дает фиктивного хот-дога официанту)
официант до тестовый водитель : вот ваш картофель фри (дает картофель фри из какого-то другого заказа для тестирования водителя)

тест-водитель отмечает неожиданную картошку фри: ТЕСТ НЕ УДАЛ! официант вернул неправильное блюдо

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

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

27 голосов
/ 02 сентября 2010

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

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

http://en.wikipedia.org/wiki/Mock_object

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

Слово «макет» иногда ошибочно используется как «заглушка». Различия между этими двумя словами описаны здесь . По сути, макет представляет собой объект-заглушку, который также включает в себя ожидания (то есть «утверждения») для правильного поведения тестируемого объекта / метода.

Например:

class OrderInteractionTester...
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

Обратите внимание, что фиктивные объекты warehouse и mailer запрограммированы с ожидаемыми результатами.

15 голосов
/ 02 сентября 2010

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

  • Реальный объект слишком сложен, чтобы включить его в модульное тестирование (для пример сетевого общения, вы можете иметь фиктивный объект, который симулировать был другой сверстник)
  • Результат вашего объекта не детерминированный
  • Реальный объект еще не доступен
12 голосов
/ 02 сентября 2010

Макет объекта является одним из типов Test Double .Вы используете mockobjects для тестирования и проверки протокола / взаимодействия тестируемого класса с другими классами.

Как правило, вы будете иметь вид «программных» или «рекордных» ожиданий: вызовы методов, которые вы ожидаете от своего класса,базовый объект.

Допустим, например, что мы тестируем сервисный метод для обновления поля в виджете.И что в вашей архитектуре есть WidgetDAO, который имеет дело с базой данных.Разговор с базой данных идет медленно, а настройка и последующая очистка усложняются, поэтому мы смоделируем WidgetDao.

давайте подумаем, что должен делать сервис: он должен получить виджет из базы данных, что-то сделать си сохраните его снова.

Таким образом, в псевдо-языке с псевдо-фиктивной библиотекой у нас было бы что-то вроде:

Widget sampleWidget = new Widget();
WidgetDao mock = createMock(WidgetDao.class);
WidgetService svc = new WidgetService(mock);

// record expected calls on the dao
expect(mock.getById(id)).andReturn(sampleWidget);   
expect(mock.save(sampleWidget);

// turn the dao in replay mode
replay(mock);

svc.updateWidgetPrice(id,newPrice);

verify(mock);    // verify the expected calls were made
assertEquals(newPrice,sampleWidget.getPrice());

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

11 голосов
/ 02 сентября 2010

Я настоятельно рекомендую отличную статью Мартина Фаулера , объясняющую, что именно является насмешками и чем они отличаются от заглушек.

9 голосов
/ 02 сентября 2010

При модульном тестировании какой-либо части компьютерной программы в идеале вы хотите протестировать только поведение этой конкретной части.

Например, посмотрите на псевдокод ниже из воображаемого фрагмента программы, которая использует другую программу для вызова печати чего-либо:

If theUserIsFred then
    Call Printer(HelloFred)
Else
   Call Printer(YouAreNotFred)
End

Если вы тестировали это, вы бы хотели проверить часть, которая проверяет, является ли пользователь Фредом или нет. Вы действительно не хотите проверять Printer часть вещей. Это будет еще один тест.

Это - это то место, где появляются ложные объекты. Они притворяются другими типами вещей. В этом случае вы бы использовали Mock Printer, чтобы он действовал так же, как настоящий принтер, но не делал бы неудобных вещей, таких как печать.


Существует несколько других типов притворных объектов, которые вы можете использовать, которые не являются Mocks. Главное, что делает Mocks Mocks, - это то, что их можно настроить с учетом поведения и ожиданий.

Ожидания позволяют вашему Mock выдавать ошибку при неправильном использовании. Таким образом, в приведенном выше примере вы можете быть уверены, что Printer вызывается с HelloFred в тестовом примере "user is Fred". Если этого не произойдет, ваш издеватель может предупредить вас.

Поведение в Mocks означает, что, например, ваш код сделал что-то вроде:

If Call Printer(HelloFred) Returned SaidHello Then
    Do Something
End

Теперь вы хотите проверить, что делает ваш код, когда вызывается принтер и возвращает SaidHello, поэтому вы можете настроить Mock на возврат SaidHello, когда он вызывается с HelloFred.

Один хороший ресурс об этом - пост Мартина Фаулера Насмешки - это не заглушки

7 голосов
/ 02 сентября 2010

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

Короче говоря, вы используете заглушки для разрушения SUT (System Test) зависимость от других объектов и макеты, чтобы сделать это и , чтобы убедиться, что SUT вызвал определенные методы / свойства для зависимости.Это восходит к фундаментальным принципам модульного тестирования - тесты должны быть легко читаемыми, быстрыми и не требующими настройки, что может подразумевать использование всех реальных классов.

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

Простой сценарий с использованием C # и Moq:

public interface IInput {
  object Read();
}
public interface IOutput {
  void Write(object data);
}

class SUT {
  IInput input;
  IOutput output;

  public SUT (IInput input, IOutput output) {
    this.input = input;
    this.output = output;
  }

  void ReadAndWrite() { 
    var data = input.Read();
    output.Write(data);
  }
}

[TestMethod]
public void ReadAndWriteShouldWriteSameObjectAsRead() {
  //we want to verify that SUT writes to the output interface
  //input is a stub, since we don't record any expectations
  Mock<IInput> input = new Mock<IInput>();
  //output is a mock, because we want to verify some behavior on it.
  Mock<IOutput> output = new Mock<IOutput>();

  var data = new object();
  input.Setup(i=>i.Read()).Returns(data);

  var sut = new SUT(input.Object, output.Object);
  //calling verify on a mock object makes the object a mock, with respect to method being verified.
  output.Verify(o=>o.Write(data));
}

В приведенном выше примере я использовал Moq для демонстрации заглушеки издевается.Moq использует один и тот же класс для обоих - Mock<T>, что немного сбивает с толку.Несмотря на это, во время выполнения тест не пройден, если output.Write не вызывается с данными как parameter, тогда как сбой при вызове input.Read() не приведет к его отказу.

4 голосов
/ 02 сентября 2010

В качестве другого ответа, предложенного по ссылке на Mocks - это не заглушки , mocks - это форма «test double», которую можно использовать вместо реального объекта.Что отличает их от других форм тестовых двойников, таких как объекты-заглушки, так это то, что другие тестовые двойники предлагают проверку состояния (и, возможно, моделирование), тогда как макеты предлагают проверку поведения (и, возможно, моделирование).Вы можете вызвать несколько методов в заглушке в любом порядке (или даже многократно) и определить успех, если заглушка захватила значение или состояние, которое вы намеревались.Напротив, фиктивный объект ожидает вызова очень определенных функций в определенном порядке и даже определенное количество раз.Тест с фиктивным объектом будет считаться «неудачным» просто потому, что методы были вызваны в другой последовательности или количестве - даже если фиктивный объект находился в правильном состоянии после завершения теста!

Таким образом, макетобъекты часто считаются более тесно связанными с кодом SUT, чем объекты-заглушки.Это может быть хорошо или плохо, в зависимости от того, что вы пытаетесь проверить.

3 голосов
/ 02 сентября 2010

Частью использования фиктивных объектов является то, что они не должны быть реально реализованы в соответствии со спецификацией. Они могут просто дать фиктивные ответы. Например. если вам нужно реализовать компоненты A и B и оба «вызывать» (взаимодействовать) друг с другом, то вы не сможете проверить A, пока не будет реализован B, и наоборот. В тестовой разработке это проблема. Таким образом, вы создаете фиктивные («фиктивные») объекты для A и B, которые очень просты, но они дают некоторый ответ, когда с ними взаимодействуют. Таким образом, вы можете реализовать и протестировать A, используя фиктивный объект для B.

1 голос
/ 14 июля 2016

Для php и phpunit это хорошо объяснено в документации phpunit. посмотреть здесь phpunit документация

В простом слове mocking object - это просто фиктивный объект вашего исходного объекта, который возвращает свое возвращаемое значение, это возвращаемое значение можно использовать в тестовом классе

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...