Какова цель макетов объектов?

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

10 ответов


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

Тестирование

представьте себе модульное тестирование для этой системы:

cook <- waiter <- customer

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

cook <- test driver

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

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

cook <- waiter <- test driver

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

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

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

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

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

Mock Объектов

теперь тестовый повар (test double) может быть реализован по-разному:

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

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

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

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

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

разговор вокруг теста на основе макета может выглядеть так:

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

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

водитель: тест Удалось!

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

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

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

водитель отмечает проблему: тест не удался! - официант изменил заказ!--7-->

или

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

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

водитель отмечает неожиданный картофель фри: тест не удался! официант вернул не то блюдо!--7-->

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

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


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

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

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 макетные объекты запрограммированы с ожидаемыми результатами.


Mock объекты являются моделируемыми объектами, которые имитируют поведение реальных. Обычно вы пишете макет объекта, если:

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

макет объекта является одним из видов Двойной Тест. Вы используете 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());

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


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


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

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

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

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

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


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

ожидания позвольте вашему макету вызвать ошибку, когда он используется неправильно. Таким образом, в приведенном выше примере вы можете быть уверены, что принтер вызывается с HelloFred в тестовом примере "пользователь-Фред". Если этого не произойдет, ваш МОК может предупредить вас.

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

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

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

один хороший ресурс вокруг этого-Martin fowlers post насмешки-это не обрубки


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

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

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

простой сценарий с использованием 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() не подведет его.


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

с заглушкой, можно назвать несколько методов на стаб в любом порядке (или даже repitiously) и определите успех, если заглушка захватила значение или состояние, которое вы намеревались. Напротив, макет-объект ожидает, что будут вызываться очень конкретные функции в определенном порядке и даже определенное количество раз. Тест с фиктивным объектом будет считаться "неудачным" просто потому, что методы были вызваны в другой последовательности или подсчете - даже если фиктивный объект имел правильное состояние, когда тест завершился!

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


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


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

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