Въведение

Често се казва, че качеството на кода, който пишете за вашите тестоветрябва да бъде поне със същото качество като производствения код.

Въпреки че съм съгласен с горното твърдение, вярвам, че стилове на програмиране и моделиза тестоветрябва да се различават от производствения код.

Защо? Защото очакваме различни неща от тестовете и ппроизводствения код.

  • Искаме тестовете да бъдат самообясняващи се. Ако даден тест се провали, не искаме да изминаваме дълъг път, за да го разберем. Обикновено сме заети да правим нещо друго.
  • Мразим каскадно неуспешни тестове. Може да сте изпитали нещо подобно:

Току-що добавих аргумент X към метод Y в клас Z. Разбирам защо тези 2 теста се провалят. Въпреки това ще прекарам следващите 3 часа в коригиране на тези 47 неуспешни теста, въпреки че промяната ми дори не е свързана.

  • Искаме/трябва да минимизираме тежестта на поддръжката на тестовете, за да продължим нашите тестове. Има присъщ силен натиск върху тестовете, защото те не са характеристики, които потребителите харесват. В крайна сметка не искаме някой (или ние самите) да се питаме „защо изобщо си правим труда да пишем тестове?“

Разработчиците прекарват стотици часове в писане на модулни тестове, защото тяхната стойност е доказана през годините. Авторите подкрепят дейността. TDD и високият процент на покритие на кода са често срещани в наши дни. Въпреки това, по-често, отколкото не, разработчиците в крайна сметка пишат огромна купчина неподдържани глупости.

Как да напиша по-добри тестове

Има няколко модела, които могат да направят вашите тестове поддържаеми. Силно препоръчвам да прочетете „Ефективна работа с модулни тестове“ от Джей Фийлдс. Ето кратко резюме на някои от идеите, обсъдени в книгата:

Не използвайте цикли във вашия тестов код

Заменете ги с индивидуални тестове. След като някой от тях се провали, ще можете лесно да откриете първопричината.

Вградена настройка

СУХОТО е добро. Да, но разбирането на тест от пръв поглед е все по-добро.

Предпочитайте настройка на линия във всеки тест пред метода на настройка.

Наличието на всичко под общ метод за настройка обикновено води до проблеми като:

  • Не цялата логика за инициализация всъщност се използва за всички тестове.
  • Придвижването нагоре и надолу във файл за проследяване на стойностите за инициализация отнема много време и губим фокус върху задачата.
  • Промяната на метода на настройка може да повреди няколко теста, които не ви интересуват.

Ако смятате, че логиката ви за инициализация става твърде сложна, вижте следващата тема.

Създател на данни

Ако създавате няколко екземпляра на тестван клас и неговите зависимости са достатъчно прости, простоизползвайте обикновен „нов“, за да ги създадете и инициализирате.

Ако откриете, че пишете сложна логика за създаване на обекти, може да си струва да създадете Data Builder. Вижте тази публикация за подробности.

Заяви последно

Може вече да сте запознати с AAA(Arrange-Act-Assert). Последното А се оказва най-важното. Дори и да не можете да преминете към пълен AAA, вашите тестове ще бъдат по-четими и поддържаеми, ако приемете модела Assert Lasts.

Хвърляния за утвърждаване

След като сте приели Assert Last, прилагането на това правило за тестове, които очакват изключения, може да е трудно, ако вашите Unit Test рамки не го поддържат.

Някои рамки поддържат ExpectedExceptionдекоратори, които можете да приложите към вашите тестови методи:

... но нарушава правилото за потвърждаване на последното.

В идеалния случай ще ни трябва нещо, както следва:

В случай че рамката по ваш избор не го поддържа, можете сами да внедрите Assert.Throws с малко усилия. [Пример]

Очаквайте литерали

Очакваните стойности трябва да бъдат възможно най-статични. Утвърждаването на литерал като низ или число е най-добрата практика, защото помага да фокусираме вниманието си върху действителнатастойност. Джей ни предупреждава за общия анти-модел за съхраняване на литерални стойности в променлива:

Очакваната стойност трябва да бъде самият литерал, а не променлива, съдържаща стойност, която преди е била създадена от литерал

Избягвайте свръхспецификация

Поддържайте тестовете си възможно най-прости. Те трябва да бъдат фокусирани върху една единствена функционалност. Добре е да имате няколко теста, които си сътрудничатс множество обекти, но трябва да има ограничен брой такива във вашия тестов пакет. Повечето от вашите тестове трябва да са наистина прости и да взаимодействат с един обект. „Законът на Деметра“ важи и тук.

ROI

Не пишете тестове на сляпо, без да мислите защо. Оценявайте всеки тест за писане от гледна точка на ROI. Дори и да можете да напишете тест за валидиране на всяка функция, не би си струвало усилието.

Стремежът към високо покритие на код е важно, но 100% е непрактично. Ако достигнете 100%покритие на кода, това означава, наред с други неща, че може да сте написали много отрицателни тестове за ROI

Заключение

Писането на ефективни модулни тестове е трудно. Правенето погрешно е много контрапродуктивно както за ефективността, така и за мотивацията на екипа. Джей Фийлдс върши страхотна работа, като показва някои насоки в книгата си „Ефективна работа с тестове на единици“ относно това какво работи по-добре в дългосрочен план. Трябва да сме наясно, че това, което е най-добра практика за производствен код, може да е анти-модел за тестов код.