*** ВНИМАНИЕ: Блог переехал на другой адрес - demin.ws ***

суббота, 2 апреля 2011 г.

Unit-тестирование для подсветки грамматики

Часто люди спорят, стоят ли все эти усилия по использованию unit-тестирования тех преимуществ в дальнейшей поддержке кода и борьбы с багами. Приводятся разные «за» и «против», и действительно есть случаи, когда преимущества unit-тестирования неочевидны. А у меня вот тут образовался весьма показательный пример.

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

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

В итоге после пары недель мытарств, я таки взял cmockery, написал всю необходимую «обвеску» и переделал все примеры в тесты.

Например:

void test_Ticket_dd6a19efa5_DATE(void **state) {
  string_eq("0029  ENTRIES<1, AB.CDE.VALUE.DATE>    = TODAY",
            "..............a........................b......");
  string_eq("0036  ENTRIES<1, AB.CDE.BOOKING.DATE>  = TODAY",
            "..............a........................b......");
  string_eq("0036  ENTRIES<1, AB.CDE.TOOKING DATE>  = TODAY",
            "..............a.................bbbb...c......");
  string_eq("0036 DATE = TODAY",
            ".....aaaa.b......");
  string_eq("0036  DATE = TODAY",
            "......aaaa.b......");
}

void test_Ticket_e8e02762a0_V_TIME(void **state) {
  string_eq("0017     V.TIME = 'x'",
            "................a.bbb");
  string_eq("0034     V.DELTA = TIME() - TIME1",
            ".................a.bbbb...c......");
}

void test_Ticket_0bcfac1fb6_READNEXT_FROM(void **state) {
  string_eq("0167     READNEXT ID FROM 9 ELSE DONE = 1 ",
            ".........aaaaaaaa....bbbb.c.dddd......e.f.");
}

И таких тестов сотни.

Макрос "string_eq" не является стандартным для cmockery, и под ним скрывается приличный кусок моего велосипеда. Вызывается функция подсветки строки, и по ней делается проход для отмечания факта смены цвета путем увеличения индекса (начальный индекс цвета - "а"). Точка значит неподсвеченный символ. Немного топорно, но позволяет не хардкодить в тестах коды конретных цветов. Конечно, сильно облегчает жизнь тот факт, что данный язык строчно-ориентированный. Иначе все было бы сложнее.

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

Каждую новую фичу (=очередной частный случай) или багфикс я начинаю с теста. И только потом код. Реально не могу представить, как бы я дальше работал над проектом без тестов.

Тут получается вообще чистая класска TDD – сначала тесты, а только потом код.

Комментариев нет:

Отправить комментарий