Как вы unit test регулярные выражения?

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

Ответ 1

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

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

Вопросы для рассмотрения:

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

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

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

refiddle.com

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

Ответ 7

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

Ответ 8

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

Ответ 9

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

Ответ 10

Используйте прибор в выбранной вами библиотеке модульных тестов и следуйте обычному подходу 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

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