Есть ли способ unit test против побочных эффектов?

Любой код может обеспечивать побочные эффекты. Большую часть времени побочные эффекты могут быть признаком плохой конструкции и/или необходимости рефакторизации, но при модульном тестировании мне сложно протестировать. Рассмотрим следующий пример:

[Test]
public void TrimAll_Removes_All_Spaces()
{
    // Arrange
    var testSubject = "A    string  with     lots   of     space";
    var expectedResult = "Astringwithlotsofspace";

    // Act
    var result = testSubject.TrimAll();

    // Assert
    Assert.AreEqual(expectedResult, result);
}

который проверяет следующее расширение:

public static string TrimAll(this string str)
{
    PokeAround();

    return str.Replace(" ", "");
}

Тест пройдет, но побочных эффектов защиты снова нет. Эффекты вызова PokeAround будут полностью незаметны.

Учитывая, что вы не знаете, что PokeAround - это может быть что угодно! - Как вы пишете тест, который защищает его? Возможно ли это?

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

public static string TrimAll(this string str)
{
    return str.Replace(" ", "");
}

Тест проходит, все хорошо. Затем, через месяц, когда я нахожусь в отпуске, коллега добавит вызов PokeAround. Я хочу, чтобы тест, который я уже написал, терпел неудачу, потому что он это сделал.

Ответ 1

Это то, что называется чувством в Эффективная работа с устаревшим кодом. То есть, воспринимая эффекты вызова тестируемого метода.

Учитывая, что вы не знаете, что такое PokeAround, это может быть что угодно!

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

Обновление:, чтобы вы могли убедиться в том, что будущие изменения в вашем тестируемом модуле никогда не будут иметь каких-либо непредвиденных побочных эффектов? Я думаю, вы

  • не может,
  • не должно.

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

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

Ответ 2

Учитывая, что вы не знаете, что такое PokeAround, это может быть что угодно! - Как вы пишете тест, который защищает его? Возможно ли это?

Этот вопрос довольно. Ситуация вряд ли произойдет в реальном мире.

  • Вы всегда знаете, что такое PokeAround. Это модульное тестирование. У вас есть источник.

    Если - через какое-то организационное зло - вам запрещено читать источник, у вас есть организационная проблема, а не техническая проблема.

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

  • Вы должны использовать Mocks для этого PokeAround, чтобы вы могли наблюдать побочные эффекты.

"защита от побочных эффектов, добавленных позже".

Это не пример таинственной части кода. Вы все еще знаете, что такое PokeAround. Вы всегда знаете, что такое PokeAround.

Вот почему мы проводим регрессионное тестирование. http://en.wikipedia.org/wiki/Regression_testing

Это все еще модульное тестирование.

  • Вы все еще проверяете PokeAround автономным unit test.

  • И вы проверяете вещи, которые используют PokeAround с макетом PokeAround.

Ответ 3

Нет из первых рук опыта от меня, но это может вас заинтересовать: Кодовые контракты

http://research.microsoft.com/en-us/projects/contracts/

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

Ответ 4

Имейте в виду, что модульные тесты - это только один инструмент в арсенале проверок и проверок, некоторые из которых могут поймать вашего коллегу "PokeAround":

  • Модульные тесты
  • Проверки кода/отзывы
  • Дизайн по контракту
  • Утверждения
  • Инструменты статического анализа, такие как FindBugs и Checker Framework (@NonNull, @Pure и @ReadOnly все рок!)

другие?

Тест проходит, все хорошо. Затем, через месяц, когда я нахожусь в отпуске, коллега добавит звонок PokeAround. Я хочу, чтобы тест, который я уже написал, терпел неудачу, потому что он это сделал.

Что заставляет вас думать, что ваш коллега тоже не изменил бы тест?

Ответ 5

Я не программирую на этом языке, но следующим способом было бы проверить, была ли изменена исходная строка:

[Test]
public void TrimAll_Removes_All_Spaces()
{
    // Arrange
    var testSubject = "A    string  with     lots   of     space";
    var testSubjectCopy = "A    string  with     lots   of     space";
    var expectedResult = "Astringwithlotsofspace";

    // Act
    var result = testSubject.TrimAll();

    // Assert
    Assert.AreEqual(expectedResult, result);

    // Check for side effects
    Assert.AreEqual(testSubjectCopy, testSubject);
}

Ответ 6

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

Ответ 7

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

Я бы предположил, что вы утверждаете, что "побочный" код не изменяется.

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

Ответ 8

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

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

Инструменты тестирования BDD/Integration также помогут в этом, так как они (обычно) тестируют большие области функциональности, а не только отдельные классы/методы.

Возможно, вам стоит взглянуть на Design By Contract (DBC). Это позволяет указывать условия pre и post, а также инварианты, так что если метод когда-либо вызывается с недопустимыми параметрами, возвращает недопустимые значения или объект попадает в недопустимое состояние, будет выдана некоторая ошибка.

Ответ 9

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

Без полного регрессионного теста побочные эффекты не могут быть найдены.

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

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

Ответ 10

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

Google Test (для С++) может сделать это; Я не знаю других фреймворков, у которых есть эта функция.