Есть ли способ провести юнит тест на побочные эффекты? - PullRequest
11 голосов
/ 30 июля 2010

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

[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 совершенно неизвестно, поскольку это очень маловероятный сценарий, поскольку у нас есть источник, когда мы пишем тест. Причина, по которой я задал этот вопрос, заключалась в том, чтобы найти способ защиты от побочных эффектов, добавленных позже . То есть, когда я пишу тест, у меня может быть такой метод расширения:

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

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

Ответы [ 10 ]

12 голосов
/ 30 июля 2010

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

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

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

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

  1. не могу,
  2. не должны.

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

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

3 голосов
/ 10 декабря 2010

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

  1. Юнит-тесты
  2. Проверка кодов / Отзывы
  3. Дизайн по контракту
  4. 1010 * Утверждения *
  5. Инструменты статического анализа, такие как FindBugs и Checker Framework (@NonNull, @Pure и @ReadOnly - все!)

другие

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

С чего вы взяли, что ваш коллега также не изменит тест?

2 голосов
/ 30 июля 2010

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

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

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

2 голосов
/ 30 июля 2010

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

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

  1. Вы всегда знаете, что такое PokeAround. Это юнит-тестирование. У вас есть источник.

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

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

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

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

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

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

Это все еще юнит-тестирование.

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

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

1 голос
/ 10 декабря 2010

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

[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);
}
0 голосов
/ 30 июля 2010

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

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

0 голосов
/ 30 июля 2010

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

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

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

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

0 голосов
/ 30 июля 2010

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

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

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

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

0 голосов
/ 30 июля 2010

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

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

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

0 голосов
/ 30 июля 2010

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

...