Есть ли способ модульного тестирования против побочных эффектов?

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

[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 звонок. Я хочу тест я уже писал не потому, что он сделал.

10 ответов


Это то, что называется зондирования на Эффективная Работа С Устаревшим Кодом. То есть, ощущая последствия вызова тестируемого метода.

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

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

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

  1. не могу
  2. не стоит.

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

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


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

  1. тесты
  2. Код Инспекции / Обзоры
  3. оформление по договору
  4. утверждения
  5. инструменты статического анализа, такие как FindBugs и Checker Framework (@NonNull, @Pure и @ReadOnly все рок!)

другим?

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

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


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

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

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


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

этот вопрос некорректен. В реальном мире такое вряд ли произойдет.

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

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

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

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

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

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

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

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

  • вы до сих пор тест PokeAround с автономного модульного теста.

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


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

[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);
}

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


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

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

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


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

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

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

одна вещь, которую вы, возможно, захотите посмотреть, это Оформление По Договору (DBC). Это позволяет указать условия pre и post, а также инварианты, так что если метод когда-либо вызывается с недопустимыми параметрами, возвращает недопустимые значения или объект попадает в недопустимое состояние, будет выдана ошибка.


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

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

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

надеюсь, что это помогает.


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

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