Как вы тестируете регулярные выражения? - PullRequest
52 голосов
/ 28 января 2009

Я новичок в TDD, и нахожу RegExp довольно частным случаем. Есть ли какой-то особый способ их модульного тестирования или я могу просто рассматривать их как обычные функции?

Ответы [ 10 ]

83 голосов
/ 28 января 2009

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

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

Очки для рассмотрения:

  • Думайте о каждой группе (скобках) как о фигурной скобке.
  • Думай о каждом | как условие. Обязательно проверяйте для каждой ветви.
  • Думайте о каждом модификаторе (*, +,?) Как об отдельном пути.
  • (примечание к вышесказанному: помните разницу между *, +,? И * ?, +? И ??.)
  • для \ d, \ s, \ w и их отрицаний, попробуйте несколько в каждом диапазоне.
  • Для * и + вам нужно проверить «нет значения», «один из» и «один или несколько» для каждого.
  • Для важных «управляющих» символов (например, строк в искомом регулярном выражении) проверьте, что произойдет, если они появятся в неправильных местах. Это может вас удивить.
  • Если у вас есть данные реального мира, используйте их как можно больше.
  • Если вы этого не сделаете, обязательно протестируйте как простые, так и сложные формы, которые должны быть действительными.
  • Обязательно проверьте, что делают управляющие символы регулярных выражений при вставке.
  • Убедитесь, что пустая строка правильно принята / отклонена.
  • Убедитесь, что строка каждого из различных видов пробелов правильно принята или отклонена.
  • Убедитесь, что выполнена правильная обработка нечувствительности к регистру (флаг i). Это разбило меня больше, чем почти все остальное при разборе текста (кроме пробелов).
  • Если у вас есть параметры x, m или s, убедитесь, что вы понимаете, что они делают, и проверьте их (поведение здесь может быть другим)

Для регулярного выражения, которое возвращает списки, также помните:

  • Убедитесь, что ожидаемые данные возвращены в правильном порядке в правильные поля.
  • Убедитесь, что небольшие изменения не возвращают хорошие данные.
  • Убедитесь, что смешанные анонимные группы и именованные группы анализируются правильно (например, (?<name> thing1 ( thing2) )) - это поведение может отличаться в зависимости от используемого вами механизма регулярных выражений.
  • Еще раз проведите множество испытаний в реальном мире.

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

В зависимости от реализации вашей библиотеки регулярных выражений способ захвата групп также может быть разным. Perl 5 имеет порядок «открытый порядок», в C # это частично, за исключением именованных групп и так далее. Не забудьте поэкспериментировать с вашим ароматом, чтобы точно знать, что он делает.

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

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

11 голосов
/ 28 января 2009

Просто добавьте в него кучу значений, проверяя, что вы получите правильный результат (будь то совпадение / отсутствие совпадения или конкретное значение замены и т. Д.).

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

9 голосов
/ 28 января 2009

Предположительно, ваши регулярные выражения содержатся в методе класса. Например:

public bool ValidateEmailAddress( string emailAddr )
{
    // Validate the email address using regular expression.
    return RegExProvider.Match( this.ValidEmailRegEx, emailAddr );
}

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

3 голосов
/ 28 января 2009

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

Кроме того, я могу полностью порекомендовать бесплатный Regex Tool Expresso . Это фантастический редактор / отладчик регулярных выражений, который избавил меня от мучительных дней.

2 голосов
/ 26 сентября 2013

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

refiddle.com

Позволяет проверять ваши регулярные выражения. Вы определяете некоторый текст, содержащий строки, которым он должен соответствовать, и строки, которым он не должен соответствовать, и если он весь зеленый, то все хорошо. Например, вот что я сделал, чтобы соответствовать слизнякам: http://refiddle.com/by/callum-locke/slug-matcher

2 голосов
/ 28 января 2009

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

1 голос
/ 05 мая 2010

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

1 голос
/ 28 января 2009

Мне нравится проверять регулярное выражение с противоположным регулярным выражением, я выполню оба с возможным тестом и проверю, чтобы пересечение было пустым.

1 голос
/ 28 января 2009

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

0 голосов
/ 16 мая 2019

Используйте прибор в выбранной вами библиотеке модульных тестов и следуйте обычному подходу TDD:

  • Проверка: тесты зеленые
  • Прервите тесты, добавив тест для следующей «функции»
  • Сделайте его зеленым, настроив регулярное выражение (не нарушая существующие тесты)
  • Refactor regex для лучшей читаемости (например, именованные группы, классы символов вместо диапазонов символов, ...)

Вот пример заглушки приспособления для спока в качестве тестового бегуна:

@Grab('org.spockframework:spock-core:1.3-groovy-2.5')
@GrabExclude('org.codehaus.groovy:groovy-nio')
@GrabExclude('org.codehaus.groovy:groovy-macro')
@GrabExclude('org.codehaus.groovy:groovy-sql')
@GrabExclude('org.codehaus.groovy:groovy-xml')

import spock.lang.Unroll

class RegexSpec extends spock.lang.Specification {
  String REGEX = /[-+]?\d+(\.\d+)?([eE][-+]?\d+)?/

  @Unroll
  def 'matching example #example for case "#description" should yield #isMatchExpected'(String description, String example, Boolean isMatchExpected) {
    expect:
    isMatchExpected == (example ==~ REGEX)

    where:
    description                                  | example        || isMatchExpected
    "empty string"                               | ""             || false
    "single non-digit"                           | "a"            || false
    "single digit"                               | "1"            || true
    "integer"                                    | "123"          || true
    "integer, negative sign"                     | "-123"         || true
    "integer, positive sign"                     | "+123"         || true
    "float"                                      | "123.12"       || true
    "float with exponent extension but no value" | "123.12e"      || false
    "float with exponent"                        | "123.12e12"    || true
    "float with uppercase exponent"              | "123.12E12"    || true
    "float with non-integer exponent"            | "123.12e12.12" || false
    "float with exponent, positive sign"         | "123.12e+12"   || true
    "float with exponent, negative sign"         | "123.12e-12"   || true
  }
}

Он может быть запущен как отдельный скриптовый скрипт типа

groovy regex-test.groovy

Отказ от ответственности: фрагмент взят из серии сообщений в блоге, которые я написал несколько недель назад

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...