Часто люди спорят, стоят ли все эти усилия по использованию 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 – сначала тесты, а только потом код.
Комментариев нет:
Отправить комментарий