Към съдържанието
wannacode

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

Накратко: Покритието на операторите пита дали всеки ред код е изпълнен. Покритието на разклоненията пита дали всяко решение е минало и по двата си пътя. За функцията за доставката първото иска 2 теста, второто 3. 100 % разклонения включва 100 % оператори, но не и обратното. И никое покритие не казва, че резултатът е проверен или че границата е на мястото си.

Проблем

100 % покритие, грешна такса

Програмистът пуска тестовете на доставката с инструмент, който брои кои редове са изпълнени. Тестовете са два: 0 € и 60 €. Отчетът казва: 100 % от редовете.

На следващия ден клиентите с количка за 30 € плащат 4,99 € доставка, вместо 4,90 €. Редът с таксата беше изпълнен. Никой тест не видя какво пише в него.

Идеята

Бялата кутия

Досега тестовете идваха от правилото: това е черната кутия. Техниките на бялата кутия (white-box) взимат тестовете от кода: кои редове има и по кои пътища може да мине програмата.

Не трябва да можеш да пишеш код за този урок. Трябва да можеш да прочетеш функцията за доставката. Езикът е по избор, в табовете:

static String delivery(double total) {
if (total <= 0) {
return "отказана";
}
double fee = 4.90;
if (total >= 50) {
fee = 0;
}
return String.format("%.2f €", fee);
}

Функцията има две решения (места, където програмата избира път): „сумата е 0 или по-малко“ и „сумата е от 50 нагоре“. Второто е „ако“ без „иначе“: ако е невярно, програмата просто прескача реда, който нулира таксата.

Идеята

Покритие на операторите

Покритието на операторите (statement coverage) пита: колко от редовете, които правят нещо, е изпълнил поне един тест?

Ето двата теста от началото, като извиквания на функцията, с изхода им:

System.out.println("0 € → " + delivery(0));
System.out.println("60 € → " + delivery(60));
Изход
0 € → отказана
60 € → 0.00 €
  • 0 € минава през първата проверка и връща „отказана“.
  • 60 € минава през всичко останало: таксата става 4,90, после проверката „от 50“ я прави 0, после връщането.

Всеки ред е изпълнен. Покритието на операторите е 100 %, с два теста.

Идеята

Покритие на разклоненията

Покритието на разклоненията (branch coverage) пита друго: всяко решение минало ли е и по двата си пътя?

Разклоненията тук са четири: първата проверка вярна и невярна, втората вярна и невярна. 0 € минава първата като вярна. 60 € минава първата като невярна и втората като вярна. Втората като невярна не я минава никой: това е празният път на „ако“ без „иначе“. Покритието е 3 от 4, тоест 75 %.

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

System.out.println("0 € → " + delivery(0));
System.out.println("30 € → " + delivery(30));
System.out.println("60 € → " + delivery(60));
Изход
0 € → отказана
30 € → 4.90 €
60 € → 0.00 €

Три теста, 100 % от разклоненията. И отговорът на въпроса на урока: за 100 % покритие на разклоненията на функцията за доставката трябват 3 теста.

По-точно, за любопитните

ISTQB CTFL v4.0.1 описва двете мерила в раздел 4.3: statement testing и branch testing. Покритието е брой изпълнени оператори (или минати разклонения) върху всички, в проценти. Програмата казва и връзката им: 100 % покритие на разклоненията включва 100 % покритие на операторите, но не и обратното.

Има и по-строги мерила: покритие на условията (всяко условие вътре в решението е видяно и вярно, и невярно) и MC/DC (всяко условие е показало, че само то може да смени решението). MC/DC се иска за най-критичния софтуер в авиацията. Те не са в CTFL v4.0.

Покритието се мери с инструмент: JaCoCo за Java, coverlet за C#, coverage.py за Python, c8 или вграденото в Node за JavaScript. Тестовете се пускат, а инструментът отбелязва кои редове и разклонения са минати.

Пробвай

Колко теста?

1. Тест с количка за 0 €. Кои части на функцията изпълнява?
2. Тестовете са 0 € и 60 €. Какво е покритието на разклоненията?
3. Кой тест довежда разклоненията до 100 %?

Идеята

Защо 100 % оператори пропусна таксата

Ето функцията от началото, с таксата 4,99:

static String delivery(double total) {
if (total <= 0) {
return "отказана";
}
double fee = 4.99;
if (total >= 50) {
fee = 0;
}
return String.format("%.2f €", fee);
}

С двата теста за операторите изходът е същият като на правилната функция:

System.out.println("0 € → " + delivery(0));
System.out.println("60 € → " + delivery(60));
Изход
0 € → отказана
60 € → 0.00 €

При 60 € редът с таксата се изпълнява, но веднага след него таксата става 0. Грешната стойност никога не стига до изхода. Изпълнен не значи проверен.

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

System.out.println("0 € → " + delivery(0));
System.out.println("30 € → " + delivery(30));
System.out.println("60 € → " + delivery(60));
Изход
0 € → отказана
30 € → 4.99 €
60 € → 0.00 €

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

Идеята

Какво покритието не казва

Ето проверката на лимита в количката: добавянето се отказва, ако е по-малко от 1 бройка или ако в количката ще станат повече от 10.

static boolean canAdd(int inCart, int add) {
if (add < 1 || inCart + add > 10) {
return false;
}
return true;
}

Две извиквания дават 100 % от разклоненията: (5, 0) минава през „отказ“, (2, 3) през „прието“.

System.out.println(canAdd(5, 0));
System.out.println(canAdd(2, 3));
Изход
false
true

Сега смени „> 10“ на „> 11“. Същите два теста минават, покритието е пак 100 %, а 11-ата бройка влиза в количката. Покритието мери какво е изпълнено, не колко близо до границата. Тестът (10, 1) от урока за граничните стойности хваща грешката.

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

Упражнение

Покритието на лимита

1. Две условия, едно решение. Въпросите са за проверката на лимита по-горе.

1. Колко теста трябват за 100 % покритие на разклоненията на проверката за лимита?
2. В кода „> 10“ е сменено на „> 11“. Тестовете (5, 0) и (2, 3) дават ли 100 % разклонения?
3. При тестовете (5, 0) и (2, 3) частта „в количката плюс добавяното е над 10“ изобщо изпълнена ли е?

Тест

Провери се

1. Откъде идват тестовете при техниките на бялата кутия?
2. Кое е вярно?
3. Тестовете дават 100 % покритие на операторите, а такса 4,99 € вместо 4,90 € минава незабелязана. Защо?
4. Какво не може да каже никое покритие на кода?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·