Что такое строгие и нестрогие моки?

Я начал использовать moq для издевательства. Может кто-нибудь объяснить мне концепцию строгих и нестрогих моков? Как их можно использовать в moq?

edit: в каком сценарии мы используем макет?


person Sandbox    schedule 28.06.2010    source источник
comment
Было интересно, совпадает ли строгий и нестрогий с разницей между строгими модульными тестами и свободными (черными ящиками) модульными тестами, описанными в https://docs.angularjs.org/api/ngMock/service/%24httpBackend   -  person The Red Pea    schedule 15.12.2016


Ответы (4)


Я не уверен насчет moq конкретно, но вот как строгие mocks работают в Rhino. Я заявляю, что ожидаю вызова foo.Bar моего объекта foo:

foo.Expect(f => f.Bar()).Returns(5);

Если вызывающий код

foo.Bar();

тогда я в порядке, потому что ожидания полностью оправдались.

Однако, если код вызова:

foo.Quux(12);
foo.Bar();

тогда мои ожидания не оправдались, потому что я явно не ожидал вызова foo.Quux.

Подводя итог, строгий макет не удастся сразу же, если что-то отличается от ожиданий. С другой стороны, нестрогий макет (или заглушка) с радостью «проигнорирует» вызов foo.Quux и должен вернуть default(T) для типа возврата T из foo.Quux.

Создатель Rhino строгое (и предпочитаете заглушки), потому что обычно вы не хотите, чтобы тест завершался неудачно при получении неожиданного вызова, как указано выше. Это значительно усложняет рефакторинг вашего кода, когда вам нужно исправить десятки тестов, основанных на точном исходном поведении.

person Mark Rushakoff    schedule 28.06.2010
comment
где вы указали, что это строгий макет? - person Sandbox; 28.06.2010
comment
@Sandbox: вы должны указать строгий или нестрогий в конструкторе Mock, который принимает MockBehavior аргумент. Поведение по умолчанию (без указания MockBehavior) кажется нестрогим (они называют это свободным). - person Mark Rushakoff; 28.06.2010
comment
Думаю, было бы хорошо включить комментарий Марка в сам ответ; как объяснение того, как создать строгий и нестрогий, так и упоминание о свободном псевдониме. - person The Red Pea; 15.12.2016

Когда-нибудь сталкивались с данным / Когда / Тогда?

  • Учитывая контекст
  • Когда я выполняю какие-то мероприятия
  • Тогда должен произойти исход

Этот шаблон появляется в сценариях BDD, а также актуален для модульных тестов.

Если вы настраиваете контекст, вы собираетесь использовать информацию, которую предоставляет этот контекст. Например, если вы ищете что-то по Id, это контекст. Если его нет, тест не запустится. В этом случае вы хотите использовать NiceMock или заглушку или что-то еще - способ работы Moq по умолчанию.

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

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

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

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

  • нестрогий макет: обычно
  • строгое издевательство: желательно никогда

NB: Я сильно предвзято отношусь к BDD, поэтому заядлые TDD-специалисты могут не согласиться со мной, и это будет правильным с точки зрения их работы.

person Lunivore    schedule 28.06.2010
comment
Как избежать проблем, когда метод изменяет функциональность, а затем у вас нет тестов, подтверждающих эту новую функциональность, потому что все ваши модульные тесты все еще проходят? - person Daniel Lorenz; 27.01.2018
comment
@DanielLorenz Если вы меняете функциональность, вы сначала пишете неудачный тест; это основная часть цикла TDD, независимо от того, какой макет вы используете. Если вы имеете в виду непреднамеренно изменяющиеся методы, наличие удобочитаемых тестов с тщательно продуманными обязанностями снижает вероятность того, что это произойдет в любом случае. - person Lunivore; 29.01.2018
comment
Я думаю, что лучший способ приблизиться к этому - не выполнять все ваши тесты, кроме одного строгого. Таким образом, если вы сделаете кучу подобных изменений, 1 тест не будет выполнен и напомнит вам, что вам нужно убедиться, что у вас есть все новые случаи, но тогда исправить этот один тест будет не так сложно. - person Daniel Lorenz; 29.01.2018
comment
@DanielLorenz Если вы делаете TDD правильно, он почти никогда не обнаруживает ошибок. Тем не менее, это помогает с отличным дизайном и самокомментирующим кодом. Я обнаружил, что люди, которые думают об этом как о тестировании, склонны закреплять свой код, чтобы он не сломался; Я предпочитаю, чтобы изменение было простым и безопасным. Но тогда я действительно BDDer, а не TDDer, даже на этом уровне. dannorth.net/introduction-bdd - person Lunivore; 31.01.2018
comment
Я тоже не совсем TDDer, но если он предотвращает 1 ошибку, добавляя 1 строгий тест, это уже того стоило. :) Если у вас нет строгих тестов, вы могли пропустить модульный тест, не осознавая этого. Я бы предпочел быть спокойным, что хотя бы один тест поможет мне это уловить. - person Daniel Lorenz; 31.01.2018
comment
@DanielLorenz Имейте в виду, что цель TDD - сделать что-то легким для изменения. Если вы установите очень жесткие тесты, это усложнит, а не облегчит изменение кода. Я предлагаю попробовать оба способа. Более 20 лет программирования привели меня к очень сильному предпочтению нестрогих моков. - person Lunivore; 01.02.2018

Вот хорошая статья.
Обычно у меня получается что-то вроде этого

public class TestThis {

    private final Collaborator1 collaborator1;
    private final Collaborator2 collaborator2;
    private final Collaborator2 collaborator3;

    TestThis(Collaborator1 collaborator1, Collaborator2 collaborator2, Collaborator3 collaborator3) {
        this.collaborator1 = collaborator1;
        this.collaborator2 = collaborator2;
        this.collaborator3 = collaborator3;
    }

    public Login login(String username) {
        User user = collaborator1.getUser(username);
        collaborator2.notify(user);
        return collaborator3.login(user);
    }

}

... и я использую Strict mocks для трех соавторов, чтобы проверить логин (имя пользователя). Я не понимаю, почему нельзя использовать Strict Mocks.

person mickthompson    schedule 30.06.2010

У меня есть простое соглашение:

  1. Используйте строгие макеты, когда тестируемая система (SUT) делегирует вызов нижележащему макетируемому слою без реального изменения или применения какой-либо бизнес-логики к аргументам, переданным самой себе.

  2. Используйте свободные макеты, когда SUT применяет бизнес-логику к аргументам, переданным самому себе, и передает некоторые производные / измененные значения на макетированный уровень.

Например: Допустим, у нас есть поставщик базы данных StudentDAL, который имеет два метода:

Интерфейс доступа к данным выглядит примерно так:

public Student GetStudentById(int id);
public IList<Student> GetStudents(int ageFilter, int classId);

Реализация, использующая этот DAL, выглядит следующим образом:

public Student FindStudent(int id)
{
   //StudentDAL dependency injected
   return StudentDAL.GetStudentById(id);
   //Use strict mock to test this
}
public IList<Student> GetStudentsForClass(StudentListRequest studentListRequest)
{
  //StudentDAL dependency injected
  //age filter is derived from the request and then passed on to the underlying layer
  int ageFilter = DateTime.Now.Year - studentListRequest.DateOfBirthFilter.Year;
  return StudentDAL.GetStudents(ageFilter , studentListRequest.ClassId)
  //Use loose mock and use verify api of MOQ to make sure that the age filter is correctly passed on.

}
person Amol    schedule 22.10.2013