01. Прошлая и настоящая жизнь
Привет, я JUnit, среда модульного тестирования Java с открытым исходным кодом. Прежде чем познакомиться со мной, давайте сначала разберемся, что такое модульное тестирование. Модульное тестирование — это написание тестового кода для наименьшего функционального блока. В Java наименьшая функциональная единица — это метод, поэтому модульное тестирование Java-программиста фактически проверяет метод Java.
Почему юнит-тест? Потому что модульное тестирование может гарантировать, что код, который вы пишете, соответствует требованиям программного обеспечения и спецификациям разработки. Модульное тестирование — это самый низкий тип тестирования среди всех тестов, это первое и самое важное звено, это единственный тест, который может обеспечить 100% покрытие кода и является основой и предпосылкой всего процесса тестирования программного обеспечения. Можно сказать, что модульное тестирование — это лучшее соотношение цены и качества.
Такая статистика у Microsoft и раньше была: среднее время, затрачиваемое на поиск багов на этапе модульного тестирования, составляет 3,25 часа, а если их не допустить до системного тестирования, то на это уходит 11,5 часов.
Сказав это, вы уже должны знать о важности модульного тестирования. Часто ли вы делали это, когда впервые писали тестовый код? Как показано ниже.
public class Factorial {
public static long fact(long n) {
long r = 1;
for (long i = 1; i <= n; i++) {
r = r * i;
}
return r;
}
public static void main(String[] args) {
if (fact(3) == 6) {
System.out.println("通过");
} else {
System.out.println("失败");
}
}
}
тестироватьfact()
правильность метода, выmain()
В методе написан тестовый код. Если вы сделали это, я могу только сказать, что когда-то вы были молоды и невинны! использоватьmain()
У метода тестирования есть много недостатков, таких как:
1) Тестовый код не отделен от исходного кода.
2) Недостаточно гибкий для написания общего набора тестового кода.
3) Ожидаемые и фактические результаты не могут быть автоматически распечатаны, и нет возможности их сравнить.
Но если вы научитесь использовать меня - JUnit, у вас больше не будет этой проблемы. Я могу очень просто организовать тестовые коды, запускать их в любое время и давать точные отчеты о тестировании, позволяя вам в кратчайшие сроки выяснить, что не так с вашим кодом.
02. Руководство по началу работы
Ну, теперь, когда вы знаете, насколько я хорош, чего вы ждете, давайте начнем! Моя последняя версия — JUnit 5, интегрированная в Intellij IDEA, так что вы можете писать и запускать мои тестовые примеры прямо в IDEA.
Первый шаг — нажать прямо в текущем окне редактора кодаCommand+N
(версия для Mac) выберите «Тестировать...» во всплывающем меню.
Проверьте метод написания тестовых случаевfact()
, а затем нажмите кнопку ОК.
На этом этапе IDEA автоматически создаст тестовый класс с именем класса Test (условие) в пакете, в котором находится текущий класс. Как показано ниже.
Если вы используете меня впервые, IDEA предложит вам импортировать мои зависимости. Рекомендуется выбрать последнюю версию JUnit 5.4.
После завершения импорта вы можете открыть файл pom.xml, чтобы убедиться в наличии дополнительных зависимостей от меня.
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
На втором этапе добавьте набор утверждений в метод тестирования, как показано ниже.
@Test
void fact() {
assertEquals(1, Factorial.fact(1));
assertEquals(2, Factorial.fact(2));
assertEquals(6, Factorial.fact(3));
assertEquals(100, Factorial.fact(5));
}
@Test
Аннотация - это то, что я просил, я добавлю его с@Test
метод идентифицируется как метод испытаний. Внутри метода тестирования вы можете использоватьassertEquals()
Сравните ожидаемое значение с фактическим значением.
На третьем шаге вы можете выбрать «Запустить FactorialTest» в меню почты, чтобы запустить тестовый пример, и результат показан ниже.
Тест не пройден, потому что ожидаемый результат в строке 20 не соответствует фактическому результату, ожидаемый результат равен 100, а фактический результат равен 120. На этом этапе вы либо исправляете код реализации, либо исправляете тестовый код, пока тест не пройдет.
Не сложно, правда? Модульное тестирование гарантирует, что один метод ведет себя так, как ожидается, и если вы изменяете код метода, вам нужно только убедиться, что соответствующий модульный тест проходит, и вы можете считать изменение в порядке.
03. Заглядывая вперед
В тестовом случае можно протестировать несколько методов. Перед тестированием нужно подготовить некоторые условия, например создание объектов, после тестирования нужно уничтожить эти объекты, чтобы высвободить ресурсы. Повторение этого шаблонного кода в нескольких методах тестирования было бы очень подробным.
В это время, что мы должны делать?
я предоставляю вамsetUp()
иtearDown()
, как культурный человек, я называю это "с нетерпением жду будущего". Давайте посмотрим на код для тестирования.
public class Calculator {
public int sub(int a, int b) {
return a - b;
}
public int add(int a, int b) {
return a + b;
}
}
Не забудьте проверить при создании нового тестового примераsetUp
иtearDown
.
Сгенерированный код показан ниже.
class CalculatorTest {
Calculator calculator;
@BeforeEach
void setUp() {
calculator = new Calculator();
}
@AfterEach
void tearDown() {
calculator = null;
}
@Test
void sub() {
assertEquals(0,calculator.sub(1,1));
}
@Test
void add() {
assertEquals(2,calculator.add(1,1));
}
}
@BeforeEach
изsetUp()
метод будет запускаться каждый@Test
метод запускался раньше;@AfterEach
изtearDown()
метод будет запускаться каждый@Test
метод запускается после.
Соответствующее ему также@BeforeAll
и@AfterAll
,и@BeforeEach
и@AfterEach
Разница в том, что All обычно используется для инициализации и уничтожения статических переменных.
public class DatabaseTest {
static Database db;
@BeforeAll
public static void init() {
db = createDb(...);
}
@AfterAll
public static void drop() {
...
}
}
03. Аномальный тест
Обработка исключений также очень важна для программ Java. Тестирование возможных исключений само по себе является важной частью тестирования.
Также возьмите предыдущий класс Factorial для иллюстрации. существуетfact()
В начале метода проверяется параметр n, и если он меньше 0, выбрасывается исключение IllegalArgumentException.
public class Factorial {
public static long fact(long n) {
if (n < 0) {
throw new IllegalArgumentException("参数不能小于 0");
}
long r = 1;
for (long i = 1; i <= n; i++) {
r = r * i;
}
return r;
}
}
Добавить тестовый метод в FactorialTestfactIllegalArgument()
.
@Test
void factIllegalArgument() {
assertThrows(IllegalArgumentException.class, new Executable() {
@Override
public void execute() throws Throwable {
Factorial.fact(-2);
}
});
}
я предлагаю вамassertThrows()
метод, первый параметр — это тип исключения, а второй параметр — Executable, который может инкапсулировать код, генерирующий исключение. Если вы считаете, что анонимные внутренние классы сложнее писать, вы можете использовать лямбда-выражения.
@Test
void factIllegalArgumentLambda() {
assertThrows(IllegalArgumentException.class, () -> {
Factorial.fact(-2);
});
}
04. Игнорировать тесты
Иногда, по каким-то причинам, некоторые методы имеют ошибки и требуют определенного периода времени для исправления.До исправления тестовые случаи, соответствующие методу, всегда заканчивались сбоем.Во избежание этой ситуации, я предоставляю вам@Disabled
аннотация.
class DisabledTestsDemo {
@Disabled("该测试用例不再执行,直到编号为 43 的 bug 修复掉")
@Test
void testWillBeSkipped() {
}
@Test
void testWillBeExecuted() {
}
}
@Disabled
Аннотации также могут быть необъяснимыми, но я рекомендую вам все же их предоставить и кратко объяснить, почему этот метод тестирования следует игнорировать. В приведенном выше примере, если бы остальная часть команды увидела инструкции, они бы поняли, что когда ошибка номер 43 будет исправлена, метод тестирования будет снова включен. Даже напомнить себе необходимо, потому что через долгое время вы можете забыть, почему вы изначально игнорировали этот метод тестирования.
05. Проверка состояния
Иногда вам может потребоваться запустить тестовый метод при определенных условиях и не запускать тестовый метод при некоторых условиях. Для этого варианта использования я предлагаю вам условный тест.
1) Для разных операционных систем могут потребоваться разные тестовые примеры, например пути для Linux и Windows разные.@EnabledOnOs
Аннотации могут включать разные тестовые сценарии для разных операционных систем.
@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
// ...
}
@TestOnMac
void testOnMac() {
// ...
}
@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
// ...
}
@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
// ...
}
2) Для разных сред выполнения Java также могут потребоваться разные тестовые сценарии.@EnabledOnJre
и@EnabledForJreRange
Аннотации могут удовлетворить это требование.
@Test
@EnabledOnJre(JAVA_8)
void onlyOnJava8() {
// ...
}
@Test
@EnabledOnJre({ JAVA_9, JAVA_10 })
void onJava9Or10() {
// ...
}
@Test
@EnabledForJreRange(min = JAVA_9, max = JAVA_11)
void fromJava9to11() {
// ...
}
06. Эпилог
Наконец, позвольте мне сказать вам три слова от всего сердца. При написании модульных тестов вам лучше сделать это:
1) Код самого юнит-теста должен быть очень понятным, и вы можете понять его с первого взгляда, и вы никогда не должны писать тестовый код для тестового кода.
2) Каждый модульный тест должен быть независимым друг от друга и не зависеть от порядка во время выполнения.
3) Обратите особое внимание на граничные условия при тестировании, такие как 0,null
, пустая строка "" и т.д.
Надеюсь, я смогу найти для вас ошибки в коде как можно быстрее, ведь чем раньше вы их найдете, тем меньше ущерба они нанесут. Увидимся!
Рекомендуемое чтение:
Взяв мою сестру в яму, как ей улучшить свои навыки программирования?
Потрясающие, более 115 000 руководств по Java на GitHub!
Последняя небольшая просьба, если эта статья действительно помогла вам, просто поставьте лайк, позвольте мне впервые порадоваться в 2021 году, спасибо за вашу поддержку, давайте работать вместе в новом году!