Мутационно тестване с PITest

1. Общ преглед

Тестването на софтуер се отнася до техниките, използвани за оценка на функционалността на софтуерно приложение. В тази статия ще обсъдим някои от показателите, използвани в индустрията за тестване на софтуер, като покритие на кода и тестване на мутации , със особен интерес за това как да извършим тест за мутация с помощта на библиотеката PITest .

За по-голяма простота ще базираме тази демонстрация на основна функция на палиндрома - Имайте предвид, че палиндромът е низ, който чете еднакво назад и напред.

2. Зависимости на Maven

Както можете да видите в конфигурацията на зависимостите на Maven, ние ще използваме JUnit, за да стартираме нашите тестове, и библиотеката PITest, за да въведем мутанти в нашия код - не се притеснявайте, ще видим след секунда какво е мутант. Винаги можете да потърсите най-новата версия на зависимостта спрямо централното хранилище на maven, като следвате тази връзка.

 org.pitest pitest-parent 1.1.10 pom  

За да може библиотеката PITest да работи и работи, ние също трябва да включим приставката pitest-maven в нашия конфигурационен файл pom.xml :

 org.pitest pitest-maven 1.1.10   com.baeldung.testing.mutation.*   com.baeldung.mutation.test.*    

3. Настройка на проекта

След като конфигурирахме зависимостите си от Maven, нека да разгледаме тази самообяснима функция на палиндром:

public boolean isPalindrome(String inputString) { if (inputString.length() == 0) { return true; } else { char firstChar = inputString.charAt(0); char lastChar = inputString.charAt(inputString.length() - 1); String mid = inputString.substring(1, inputString.length() - 1); return (firstChar == lastChar) && isPalindrome(mid); } } 

Всичко, от което се нуждаем сега, е прост JUnit тест, за да се уверим, че нашето внедряване работи по желания начин:

@Test public void whenPalindrom_thenAccept() { Palindrome palindromeTester = new Palindrome(); assertTrue(palindromeTester.isPalindrome("noon")); } 

Засега добре, ние сме готови да стартираме успешно нашия тест като JUnit тест.

След това в тази статия ще се съсредоточим върху покритието на кода и мутациите с помощта на библиотеката PITest.

4. Покритие на кода

Покритието на кода се използва широко в софтуерната индустрия, за да се измери какъв процент от пътищата за изпълнение е бил упражнен по време на автоматизирани тестове.

Можем да измерим ефективното покритие на кода въз основа на пътища за изпълнение, като използваме инструменти като Eclemma, налични в Eclipse IDE.

След като стартираме TestPalindrome с покритие на кода, можем лесно да постигнем 100% резултат на покритие - Имайте предвид, че isPalindrome е рекурсивен, така че е доста очевидно, че празната проверка на дължината на въвеждане ще бъде покрита така или иначе.

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

5. Покритие на мутацията

Мутационното тестване е тестова техника, използвана за подобряване на адекватността на тестовете и идентифициране на дефекти в кода. Идеята е да се променя динамично производствения код и да се провалят тестовете.

Добрите тестове ще се провалят

Всяка промяна в кода се нарича мутант и води до променена версия на програмата, наречена мутация .

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

Сега нека стартираме теста, използвайки Maven, като опцията за цел е зададена на: org.pitest: pitest-maven: mutationCoverage .

Можем да проверим отчетите в HTML формат в директорията target / pit-test / YYYYMMDDHHMI :

  • 100% покритие на линията: 7/7
  • 63% покритие на мутация: 5/8

Ясно е, че нашият тест обхваща всички пътища за изпълнение, като по този начин резултатът за покритие на линията е 100%. От друга страна, библиотеката PITest представи 8 мутанти , 5 от тях бяха убити - причинени от неуспех - но 3 оцеляха.

Можем да проверим отчета com.baeldung.testing.mutation / Palindrome.java.html за повече подробности относно създадените мутанти:



Това са мутаторите, активни по подразбиране при изпълнение на тест за покритие на мутация:

  • INCREMENTS_MUTATOR
  • VOID_METHOD_CALL_MUTATOR
  • RETURN_VALS_MUTATOR
  • MATH_MUTATOR
  • NEGATE_CONDITIONALS_MUTATOR
  • INVERT_NEGS_MUTATOR
  • CONDITIONALS_BOUNDARY_MUTATOR

За повече подробности относно мутаторите PITest можете да проверите връзката на официалната страница на документацията .

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

6. Подобрете резултата за мутация

Сега, когато знаем какво е мутация, трябва да подобрим резултата си от мутации, като убием оцелелите мутанти .

Да вземем за пример първата мутация - отречена условна - на ред 6. Мутантът оцеля, защото дори да променим кодовия фрагмент:

if (inputString.length() == 0) { return true; }

Да се:

if (inputString.length() != 0) { return true; }

Тестът ще премине и затова мутацията оцеля . Идеята е да се приложи нов тест, който ще се провали, в случай че мутантът бъде въведен . Същото може да се направи и за останалите мутанти.

@Test public void whenNotPalindrom_thanReject() { Palindrome palindromeTester = new Palindrome(); assertFalse(palindromeTester.isPalindrome("box")); } @Test public void whenNearPalindrom_thanReject() { Palindrome palindromeTester = new Palindrome(); assertFalse(palindromeTester.isPalindrome("neon")); }

Сега можем да стартираме нашите тестове с помощта на приставката за покритие на мутацията, за да сме сигурни, че всички мутации са били убити , както можем да видим в отчета PITest, генериран в целевата директория.

  • 100% покритие на линията: 7/7
  • 100% покритие на мутацията: 8/8

7. Конфигурация на PITest тестове

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

Също така е важно да определите мутаторите, които планирате да използвате по време на тестване на мутации, за да сведете до минимум изчислителните ресурси, необходими за извършване на тестовете:

  com.baeldung.testing.mutation.*   com.baeldung.mutation.test.*   CONSTRUCTOR_CALLS VOID_METHOD_CALLS RETURN_VALS NON_VOID_METHOD_CALLS  

Освен това библиотеката PITest предлага разнообразни опции, които са на разположение за персонализиране на вашите стратегии за тестване , можете да посочите максималния брой мутанти, въведени от класа, като използвате опцията maxMutationsPerClass например. Повече подробности за опциите PITest в официалното ръководство за бърз старт на Maven .

8. Заключение

Имайте предвид, че покритието на кода все още е важна метрика, но понякога не е достатъчно, за да гарантира добре тестван код. Така че в тази статия сме преминали през тестване на мутации като по-сложен начин да гарантираме качеството на тестовете и да подкрепим тестови случаи, използвайки библиотеката PITest .

Видяхме също как да анализираме основни доклади PITest, като същевременно подобряваме оценката за покритие на мутацията .

Въпреки че тестването на мутации разкрива дефекти в кода, той трябва да се използва разумно, защото това е изключително скъп и отнемащ време процес .

Можете да разгледате примерите, предоставени в тази статия в свързания проект GitHub .