Вдохновение для тестов
10 Feb 2011Много слов сказано о достоинствах юнит-тестов (TDD, BDD — в данном случае неважно), а также о том, почему люди всё-таки их не используют.
Но я думаю, что одна из главных причин заключается в том, что люди не знают, с чего начать. Вот прочитал я статью про юнит-тесты, понравилось; решил, что надо бы когда-нибудь попробовать. Но что дальше? С чего начать? Как придумывать все эти требования, как называть тест-методы?
В последнее время набирает популярность тенденция превращать юнит-тесты в BDD-спецификации, то есть говорится о том, что хороший юнит-тест должен не тестировать что-то, а описывать поведение программы. Но как описать это чёртово поведение; откуда брать вдохновение, чтобы придумать названия для всех этих тест-кейсов?
Об этом и пойдёт речь:
Откуда брать вдохновение
Однажды я пришёл к удивительному открытию: это вдохновение — повсюду, оно окружает нас каждый день. Тут и придумывать ничего не надо. Его можно просто брать и использовать. Это открытие показалось мне настолько важным, что я спешу непременно поделиться им со всеми.
Итак, мой TOP-5 источников вдохновения для написания юнит-тестов.
5. Доклад начальнику
Каждое утро на собрании начальник спрашивает тебя:
– Что ты вчера делал?
– Багу исправлял.
Начальник так просто на слово не верит и продолжает докапываться:
-— Какую багу?
-— Ну, исправил метод validateReferenceNumber.
-— Конкретнее, что ты там исправил?
-— Ну, раньше он грохался, если дать ему на входе пустую строку, а теперь он не падает, а возвращает false.
Вот оно!
Видите, практически готовый юнит-тест висит в воздухе у вас перед носом. После собрания садитесь за компьютер и пишете:
public class ReferenceNumberTest {
@Test
public void emptyStringIsNotValidReferenceNumber() {
assertFalse(validateReferenceNumber(""));
}
}
В идеале вы должны были это сделать ещё вчера, и тогда начальник мог бы посмотреть, какие юнит-тесты вы вчера добавили, и не мучать вас своими вопросами. Я не шучу, я знаю примеры весьма успешных компаний, в которых вместо Code Review делается ревью юнит-тестов.
4. Объяснения с коллегами
К вам каждый день приходит какой-нибудь коллега и спрашивает:
-— Слушай, я тут твой код дебажу-дебажу, а всё никак не могу понять, как это работает?
Ты ему терпеливо объясняешь:
-— Ну как, ну смотри: сюда приходит Б, а отсюда выходит А.
-— Всё равно не понимаю, а почему А-то?
-— Ну потому, что для всех букв, которые меньше Я, этот метод должен возвращать следующую букву.
-— Ааа, вот теперь понятно.
И снова оно!
Вы, сами того не подозревая, только что сформулировали название тест-кейса. Мысленно послав +1 в карму коллеги, сели и написали:
public class NanoTechnologySecurityTest {
@Test
public void shouldReturnNextLetterForAllLettersExceptJa() {
assertEquals("Б", encodeLetter("А"));
}
}
3. Разговор с клиентом
В любой прекрасный день клиент может позвонить и сказать:
-— Помните, мы обсуждали, что все поля на форме должны валидироваться автоматически, как только поле теряет фокус? Так вот, я передумал. Я показывал бета-версию моей бабушке, и она сказала, что это не понятно, и вообще ajax sucks. Давайте поля будут валидироваться только тогда, когда клиент нажмёт кнопку «Submit».
И опять оно!
Клиент только что сформулировал за нас текст тест-кейса.
Мысленно послав -10 в карму клиента, мы сели и написали… UI тесты, конечно, чуток сложнее, чем обычные юнит-тесты, но это могло бы выглядеть как-то так:
public class TimotiFanClubRegistrationFormTest {
@Test
public void shouldValidateFieldsOnFormSubmission() {
Form form = new Form();
assertTrue(form.isValid());
form.submit();
assertFalse(form.isSubmitted());
assertFalse(form.isValid());
form.setName("Baba Njura");
form.setEmail("Baba.Njura@yandex.ru");
form.submit();
assertTrue(form.isSubmitted());
}
}
2. Баг
Каждый день вы заходите в Jira (а кому совсем повезло — в Pivotal Tracker) и обнаруживаете, что в вашей программе зловредные тестировщики нашли-таки багу. Если вам повезло с тестировщиком, то описание баги звучит примерно так:
«Если ввести три раза неправильный пароль, то учётная запись должна заблокироваться, а у меня не блокируется. Я уже раз пятнадцать ввёл.»
Мысленно сказав тестировщику спасибо (хватит с него и спасиба, его карму всё равно не спасёшь), садимся и пишем:
public class LoginPageTest {
@Test
public void shouldBlockAccountAfter3UnsuccessfulTries() {
LoginPage page = new LoginPage();
page.login("vasjok47", "Toiota");
page.login("vasjok47", "Tojota");
page.login("vasjok47", "tayota");
assertTrue(AccountDAO.getAccount("vasjok47").isBlocked());
}
}
Есть даже отдельный термин для этого: Bug driven development. Я сам этот подход не жалую, так как юнит-тесты (они же спецификация) всё-таки должны писаться ДО кода (основной принцип TDD), но как источник вдохновения баг-трекер вполне подходит.
1. Commit message (как это по-русски?)
Это мой любимый пункт, на нём я хотел бы остановиться подробнее.
Когда вы меняете код, для этого обычно есть причина. Причина обычно заключается в том, что вы хотите, чтобы этот код что-то делал по-другому (исключаем рефакторинг, улучшение производительности и расставление скобочек по фэншую).
Допустим, был у нас код, который валидировал email. В частности, он проверял, что в конце email должна быть точка и два символа. И даже есть для него юнит-тест, всё как у людей. И тут внезапно выясняется, что после точки может быть и больше букв, например, у некоторых клиентов email заканчивается на “.info”.
Сказано-сделано, код исправили, и даже в существующий юнит-тест добавили одну строчку:
public class EmailValidatorTest {
@Test
public void testValidateEmail() {
assertTrue(validateEmail("timati@rambler.ru"));
...
assertTrue(validateEmail("tina.turner@music.info")); // 4 letters now allowed!
}
}
И теперь хотим это дело закоммитить в CVS (а кому повезло, те в SVN, а кто вообще счастливчик, те в GIT).
Для коммита надо написать пояснение (commit message), в котором обычно пишут, а что же в коде изменилось. И вот пишите вы:
svn commit -m "Теперяча мыло и на четыре буквы может заканчиваться."
И вот это самое настоящее оно-оно-оно!
Это то, что нам надо. Не нажимайте пока enter.
Остановились, выделили этот текст мышкой. Открыли класс юнит-теста. Написали Test, вставили скопированный текст и перевели на английский. Можно немножко уточнить или обобщить по вкусу.
public class EmailValidatorTest {
@Test
public void validEmailShouldEndWithDotFollowedBySeveralLetters() {
assertTrue(validateEmail("timati@rambler.ru"));
...
assertTrue(validateEmail("tina.turner@music.info"));
}
}
Вот теперь можете смело закоммитить и мысленно послать +1 себе в карму. Сегодня мы смогли материализовать некоторые знания из воздуха во что-то более ощутимое. Теперь эти знания никуда не пропадут и будут автоматически проверяться при каждой сборке проекта.
Ну не здоровско ли, а?