Эволюция Модульного Теста

О том, как правильно писать модульные тесты, и вообще о пользе сказано много слов.

ТДД .

Тогда что-то еще БДД маячил на горизонте.

Вам предстоит разобраться, какой из них лучше и в чем между ними разница.

Может быть, именно поэтому большинство разработчиков решили не заморачиваться и до сих пор не используют ни то, ни другое? Кратко: BDD — это дальнейшее развитие идей TDD, поэтому его следует использовать.

Я попытаюсь объяснить разницу между TDD и BDD на простом примере.

Давайте посмотрим на 3 ревизии одного модульного теста, которые я нашел в одном реальном проекте.



Попытка номер 1

Первая версия этого модульного теста была такой:
  
  
  
   

public class ReferenceNumberTest { @Test public void testValidate() { assertFalse( ReferenceNumber.validate("1234567890123") ); assertFalse( ReferenceNumber.validate("1234567") ); assertTrue( ReferenceNumber.validate("12345678") ); } }

Мы называем его типичный модульный тест. Он тестирует код, но это все.

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

Попытка номер 2

В какой-то момент пришел разработчик и решил применить некоторые " лучшие практики » из TDD: разбейте метод тестирования на несколько маленьких, чтобы каждый из них тестировал только что-то одно, и дайте им соответствующие имена.

Вот что он получил:

public class ReferenceNumberTest { @Test public void testTooLong() { String len13 = "1234567891111"; assertEquals(len13.length(), 13); assertEquals(ReferenceNumber.validate(len13), false); } @Test public void testTooShort() { String len7 = "1234567"; assertEquals(len7.length(), 7); assertEquals(ReferenceNumber.validate(len7), false); } @Test public void testOk() { String len8 = "12345678"; assertEquals(len8.length(), 8); assertEquals(ReferenceNumber.validate(len8), true); String len12 = "123456789111"; assertEquals(len12.length(), 12); assertEquals(ReferenceNumber.validate(len12), true); } }

Мы называем его хороший модульный тест. Читается гораздо проще: по именам переменных легко догадаться, что 13 символов — это слишком много, 7 — слишком мало, а 8 символов — это нормально.



Попытка номер 3

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

Разработчик продолжает процесс разделения и переименования:

public class ReferenceNumberTest { @Test public void nullIsNotValidReferenceNumber() { assertFalse(ReferenceNumber.validate(null)); } @Test public void referenceNumberShouldBeShorterThan13() { assertFalse(ReferenceNumber.validate("1234567890123")); } @Test public void referenceNumberShouldBeLongerThan7() { assertFalse(ReferenceNumber.validate("1234567")); } @Test public void referenceNumberShouldContainOnlyNumbers() { assertFalse(ReferenceNumber.validate("1234567ab")); assertFalse(ReferenceNumber.validate("abcdefghi")); assertFalse(ReferenceNumber.validate("---------")); assertFalse(ReferenceNumber.validate(" ")); } @Test public void validReferenceNumberExamples() { assertTrue(ReferenceNumber.validate("12345678")); assertTrue(ReferenceNumber.validate("123456789")); assertTrue(ReferenceNumber.validate("1234567890")); assertTrue(ReferenceNumber.validate("12345678901")); assertTrue(ReferenceNumber.validate("123456789012")); } }

Мы называем его спецификация в стиле БДД .

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

Чтобы понять, как работает класс, нам не обязательно лезть в код — достаточно прочитать названия.

А если при изменении кода в него была внесена ошибка и юнит-тест сломался, то по названию сломанного метода теста мы обязательно сможем определить, какая ошибка была допущена в коде.

Кстати, этот пример эволюции юнит-теста я собирался показать на семинаре.

devclub.eu по данным BDD в Таллинне.

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

Что делать? Паника! До семинара остался один день! Мне срочно нужно было самому написать это снова.

Теперь посмотрите на эти три тестовых класса и подумайте, какой из них помог мне восстановить логику класса ReferenceNumber.



И, наконец, БДД

Можно сказать, что третья версия отличается от предыдущих тем, что описывает поведение сорт. Это достигается за счет использования таких слов, как «должен» и «содержит»: «мой класс должен вести себя так», «мой метод должен делать то-то и то-то».

Итак, идея BDD именно в том, чтобы использовать слова «спецификация» и «должен» вместо слов «тестировать» и «утверждать».

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

Убедиться в этом можно, посмотрев тот же пример, переведенный с языка Юнит на языке Easyb :

description "ReferenceNumber" it "should not be null", { ReferenceNumber.validate(null).

shouldBe false } it "should be shorter than 13", { ReferenceNumber.validate("1234567890123").

shouldBe false } it "should be longer than 7", { ReferenceNumber.validate("1234567").

shouldBe false } it "should contain only numbers", { ReferenceNumber.validate("1234567ab").

shouldBe false ReferenceNumber.validate("abcdefghi").

shouldBe false ReferenceNumber.validate("---------").

shouldBe false ReferenceNumber.validate(" ").

shouldBe false } it "valid reference number examples", { ReferenceNumber.validate("12345678").

shouldBe true ReferenceNumber.validate("123456789").

shouldBe true ReferenceNumber.validate("1234567890").

shouldBe true ReferenceNumber.validate("12345678901").

shouldBe true ReferenceNumber.validate("123456789012").

shouldBe true }

Отчет о выполнении этих тестов спецификации может фактически служить документацией:

Эволюция модульного теста

Помимо «это» и «должно», в BDD есть и другие важные слова, такие как «данный», «когда» и «тогда», а также «до» и «после», а также «обеспечить», «нарратив» и «должен вести себя как».

Также BDD подходит не только для модульных тестов, но и для функциональных/интеграционных тестов, но это выходит за рамки данной статьи.

Теперь нас интересует уровень модульных тестов.

Цель данной статьи — показать, что их можно записать по-разному.

Остается добавить, что существуют библиотеки для написания спецификаций BDD для других языков: Java ( ДжейДэйв , JBehave ), Рубин ( РСпец , RBehave , Огурец ), Крутой ( Easyb ), Скала ( Scala-тест ), PHP ( Бехат ), ЦПП ( CppSpec ), .

Сеть ( SpecFlow , Должно быть ), Питон ( Латук , Огурец ).

А если по независящим от вас причинам вы не можете переключиться с JUnit на что-то другое, это тоже ничего страшного, просто запомните третий пример.

Кстати, в этом случае вам будет полезна библиотека Хармкрест .

Как завещал Козьма Прутков: товарищ, БДДи! Теги: #tdd #bdd #юнит-тесты #разработка через тестирование #разработка через поведение #tdd

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.