CodeGym /Java блог /Случаен /Тестване на единици в Java с JUnit
John Squirrels
Ниво
San Francisco

Тестване на единици в Java с JUnit

Публикувано в групата

Какво е модулно тестване в Java?

Преди да започнем да изучаваме JUnit в Java, нека направим кратък преглед на това Howво е модулно тестване и защо е толкова популярно (ако вече знаете тези неща, преминете към „Как да напиша JUnit тест в Java?“). Модулното тестване в Java прави широкомащабното разработване на софтуер много по-ефективно и безпроблемно. Може да помогне Howто на отделни лица, така и на екипи да съкратят безброй часове за отстраняване на грешки и да рационализират процеса на сътрудничество изключително много. Тестване на единици в Java с JUnit - 1

https://junit.org/junit4/

Основната идея на модулното тестване е следната: напишете атомарни тестове на отделни характеристики (наречени единици тестове) и бавно добавете още функции, след като сте тествали и сте се уверor, че предишните работят. Това е изключително проста, но мощна идея. Като пример за това How може да изглежда този процес, представете си, че създавате виртуален научен калкулатор. В допълнение към очевидните аритметични оператори ( +, -, x, %), този калкулатор ще има разширени функции, които изискват други подфункции, за да работят в него. За да изчислите експонентите, вашият калкулатор трябва да може да умножава правилно. Така че един подход за тестване на единици за изграждане и тестване на този калкулатор би бил:
  • Напишете функция за добавяне. Тествайте го внимателно, сменете го, повтаряйте, докато заработи.
  • Направете същото за функциите за изваждане, умножение, деление.
  • Използвайте тези основни оператори, за да напишете по-разширени операторни функции като експоненти, след което тествайте и тези функции.
Това гарантира, че функциите, които се основават на други по-малки подфункции, не само работят правилно сами по себе си, но нямат дефектни подфункции в себе си. Например, ако тествам експонентната функция и нещо се обърка, знам, че грешката вероятно не е в подфункцията за умножение, тъй като функцията за умножение вече беше широко тествана. Това значително елиминира общото количество code, който трябва да проследя и инспектирам, за да открия грешката. Надяваме се, че този тривиален пример изяснява How е структуриран мисловният процес около Unit Testing. Но How модулното тестване взаимодейства с останалата част от процеса на разработка на софтуер? Ами ако имате още по-сложни функции, които трябва да могат да работят и да комуникират заедно? Единичното тестване е недостатъчно, за да се гарантира, че такива сложни функции могат да работят правилно заедно. Всъщност това е само първата стъпка от четирите нива на софтуерно тестване (използвам главни букви, защото имам предвид индустриалния стандарт or най-често срещания подход за тестване на софтуер). Последните три стъпки саИнтеграционно тестване , системно тестване и тестване за приемане. Всички те вероятно означават точно това, което си мислите, че правят, но позволете ми да изясня: Интеграционното тестване е това, което бихме направor, за да гарантираме, че споменатите по-горе „сложни функции“ взаимодействат правилно помежду си. (напр. да се уверите, че калкулаторът може да се справи с „3 + 7 * 4 - 2“) Тестването на системата е тестване на цялостния дизайн на конкретна система; често има множество системи от сложни функции, работещи заедно в един продукт, така че ги групирате в системи и ги тествате поотделно. (напр. ако създавате графичен калкулатор, първо бихте изградor аритметичната „система“ за работа с числа, тествайки, докато заработи по преднаmeaning, и след това бихте изградor и тествали графичната „система“, за да се справите с оттеглянето, като ще се основава на аритметичната система). Тестването за приемане е тестване на ниво потребител; проверява дали всички системи могат да работят синхронизирано, за да създадат завършен продукт, готов да бъде приет от потребителите (напр. потребителите, които тестват калкулатора). Разработчиците на софтуер понякога могат да пренебрегнат тази последна стъпка от процеса, тъй като компаниите често ще имат други служители да внедряват потребителски (бета) тестове отделно.

Как да напиша JUnit тест в Java?

Сега, когато имате по-ясна представа за предимствата и ограниченията на модулното тестване, нека да разгледаме малко code! Ще използваме популярна рамка за тестване на Java, наречена JUnit (друга популярна е TestNG, която също можете да използвате, ако желаете. Те са много сходни синтактично; TestNG е вдъхновен от JUnit). Можете да изтеглите и инсталирате JUnit тук . За този примерен code ще продължим от примера за „научен калкулатор“, който споменах по-рано; доста е лесно да се замислите, а тестовият code е супер лесен. Конвенционалната практика е да пишете отделни тестови класове за всеки от вашите класове, така че това ще направим. Да приемем, че в този момент имаме Math.javaфайл с всички математически функции в него (включително Math.add) и пишемMathTests.javaфайл в същия пакет. Сега нека настроим изрази за импортиране и тяло на класа: (ВЪЗМОЖЕН ВЪПРОС ЗА ИНТЕРВЮ ЗА JUnit: Може да бъдете попитани къде да поставите своя JUnit тест и дали трябва or не да импортирате изходните си файлове. Ако пишете вашите тестови класове в същия пакет като вашите основни класове, тогава не се нуждаете от оператори за импортиране за вашите изходни файлове в тестовия клас. В противен случай се уверете, че импортирате вашите изходни файлове!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
Първият оператор за импортиране ни дава @Testзаглавката. Пишем „ @Test“ директно върху всяка дефиниция на тестова функция, така че JUnit да знае, че това е единичен модулен тест, който може да се изпълнява отделно. По-късно ще ви покажа How можете да изпълнявате конкретни модулни тестове, като използвате тази заглавка. Вторият оператор за импортиране ни спестява малко писане. Основната функция на JUnit, която използваме за тестване на нашите функции, е да Assert.assertEquals(), която приема два параметъра (действителна стойност и очаквана стойност) и гарантира, че са равни. Наличието на този втори оператор за импортиране ни позволява просто да напишем „ “, assertEquals(...instead of да се налага да указваме всеки път от кой пакет е част. Сега нека напишем много прост тестов случай, за да проверим дали 2 + 2 наистина е 4!

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
Нека да разгледаме всеки от петте реда на тестовата функция и Howво правят: Ред 5: Тази @Testзаглавка указва, че дефиницията на функцията по-долу add_twoPlusTwo_returnsFour()е наистина тестова функция, която JUnit може да изпълнява отделно. Ред 6: Това е сигнатурата на функцията за нашия тестов случай. Тестовите случаи винаги са много необичайни; те тестват само един конкретен пример, като например 2+2=4. Обичайно е вашите тестови случаи да се наименуват във формата " [function]_[params]_returns[expected]()", където [function]е името на функцията, която тествате, [params]са конкретните примерни параметри, които тествате, и [expected]е очакваната върната стойност на функцията. Тестовите функции почти винаги имат тип връщане „ void“, защото основната цел на цялата функция е да се изпълняваassertEquals, който ще изведе към конзолата дали вашият тест е преминал or не; нямате нужда от други данни, които да бъдат върнати никъде. Ред 7: Ние декларираме finalпроменлива „ “ от типа на връщането Math.add (int)и я наричаме „очаквана“ по конвенция. Стойността му е отговорът, който очакваме (4). Ред 8: Ние декларираме finalпроменлива „ “ от типа на връщането Math.add (int)и я наричаме „действителна“ по конвенция. Стойността му е резултат от Math.add(2, 2). Ред 9: Златната линия. Това е редът, който сравнява действителното и очакваното и ни казва, че сме преминали теста само ако са равни. Първият преминат параметър „2+2 е 4“ е описание на тестовата функция.

Ами ако моята функция трябва да хвърли изключение?

Ако вашият конкретен тестов пример трябва да хвърли изключение, instead of да твърди, че действителната и очакваната стойност са равни, тогава JUnit има начин да изясни това в заглавката @Test. Нека да разгледаме един пример по-долу. Ако приемем, че имаме Math.javaизвикана функция Math.divide, искаме да сме сигурни, че входовете не могат да бъдат разделени на 0. Вместо това, опитът за извикване Math.divide(a, 0)на която и да е стойност „a“ трябва да хвърли изключение ( ArithmeticException.class). Посочваме това в заглавката като такова:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
Можете да имате повече от едно изключение за expectedExceptions, просто се уверете, че използвате скоби и запетаи, за да посочите вашите класове изключения като такива:

expectedException = {FirstException.class, SecondException.class, … }

Как да стартирам своите JUnit тестове в Java?

Как да добавите JUnit към IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Можете да стартирате проекта си, Howто обикновено изпълнявате тестовете. Изпълнението на всички тестове в тестов клас ще ги изпълни по азбучен ред. В JUnit 5 можете да добавите приоритет към тестовете, като добавите етикет @Order. Пример:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
Въпреки че a_test()идва преди b_test()по азбучен ред и в codeа, b_test()ще се изпълнява преди a_test()тук, защото 1 идва преди 2 в реда. Така че това е всичко за основите на JUnit. Сега, нека да се заемем с няколко често срещани въпроса за интервю за JUnit и да научим малко повече за JUnit по пътя!

Въпроси за интервю за JUnit (допълнителна информация)

Тук съм събрал най-популярните въпроси за интервю за JUnit. Ако имате Howво да добавите - не се колеbyteе да го направите в коментарите по-долу. Въпрос: Какъв метод можете да извикате във вашия тестов метод, за да провалите автоматично тест? A: fail(“описание на грешката тук!”); Q: Тествате клас Dog; за да тествате обект Dog, трябва да го инстанциирате, преди да можете да изпълнявате тестове върху него. Така че пишете функция setUp(), за да създадете екземпляр на Dog. Искате да стартирате тази функция само веднъж по време на цялото тестване. Какво трябва да поставите директно над сигнатурата на функцията setUp(), така че JUnit да знае да изпълни setUp() преди да изпълни тестовете? О: @BeforeClass (@BeforeAll в JUnit 5) В:Какъв трябва да бъде сигнатурата на функцията setUp(), описана по-горе? A: публична статична празнота. Всяка функция с @BeforeClass (@BeforeAll в JUnit 5) or @AfterClass (@AfterAll в JUnit 5) трябва да е статична. Въпрос: Приключихте с тестването на класа Dog. Пишете функция void tearDown(), която почиства данните и отпечатва информация в конзолата след всеки тест. Искате тази функция да се изпълнява след всеки отделен тест. Какво трябва да поставите директно над сигнатурата на функцията tearDown(), така че JUnit да знае да изпълни tearDown() след изпълнение на всеки тест? A: @After (@AfterEach в JUnit 5)
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION