2010-07-30 2 views
10

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

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

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

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

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

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

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

+0

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

+0

@WOPR: Посмотрите мое обновление для реального сценария, где это актуально. –

+2

Это по-прежнему бессмысленно. Теперь вы пытаетесь расширить концепцию модульного тестирования, чтобы предотвратить идиотизм у других программистов. Ваш «будущий коллега» должен был также проверить его изменения. Вы не можете использовать модульное тестирование для проверки будущих изменений ... он мог бы просто удалить весь ваш код и изменить его, чтобы удалить главную загрузочную запись. – WOPR

ответ

9

Это то, что называется sensing in Working Effectively With Legacy Code. То есть, воспринимая эффекты вызова тестируемого метода.

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

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

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

  1. не может,
  2. не должен.

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

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

+4

@ Péter Török, на самом деле вам ** нужно ** протестировать сторонние библиотеки. Библиотека может и, вероятно, изменится в какой-то момент. Получив тесты, подтверждающие ваши предположения о том, как это работает, вы можете безопасно обновлять библиотеку и знать, были ли какие-либо изменения в ожидаемом поведении сразу, а не после того, как они начнут выходить на производство, когда пользователи начинают звонить. – CaffGeek

+0

Я думаю, что последнее предложение следует перефразировать до: «Если это не в какой-то третьей библиотеке, и в этом случае вы не должны * испытывать его :-)» (в идеальном мире). – strager

+0

Смотрите мое обновление для реального сценария, где это актуально. –

0

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

0

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

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

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

0

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

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

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

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

0

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

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

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

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

2

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

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

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

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

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

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

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

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

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

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

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

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

+1

* «Вы всегда знаете, что такое PokeAround. Это модульное тестирование. У вас есть источник». * - Если бы я всегда знал, что делает PokeAround (и любая другая функция, которую я кодирую), тогда у меня нет причин писать Unit -Протест вообще. Я знаю только то, что они * должны делать *. –

+1

@Luther Blissett: «Если бы я всегда знал, что делает PokeAround (и любая другая функция, которую я кодирую), тогда у меня нет причин писать Unit-Test« Что? Тестирование не является * обнаружением *. Тестирование является доказательством того, что оно действительно делает то, что оно утверждает. Тестирование - это не то, что вы делаете, чтобы обнаружить внутренние тайны кода. –

+0

См. Мое обновление для реального сценария, где это актуально. –

0

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

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

2

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

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

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

+0

О, интересно! – strager

1

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

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

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

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

прочее?

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

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

+1

Предположительно, коллега некомпетентен, не злонамерен. –

Смежные вопросы