Conditional test
JUnit5 supports conditional annotations to determine whether to execute a test based on a Boolean value.
Custom conditions
The @EnabledIf
and @DisabledIf
annotations are used to set custom conditions, example:
@Test @EnabledIf("customCondition") void enabled() { // ... } @Test @DisabledIf("customCondition") void disabled() { // ... } boolean customCondition() { return true; }
The customCondition() method is used to return a Boolean value, and it can accept a parameter of type ExtensionContext. If it is defined outside the test class, it needs to be a static method.
Built-in conditions
JUnit5’s org.junit.jupiter.api.condition
package has some built-in conditional annotations.
Operating System Conditions
@EnabledOnOs
and DisabledOnOs
, examples:
@Test @EnabledOnOs(MAC) void onlyOnMacOs() { // ... } @TestOnMac void testOnMac() { // ... } @Test @EnabledOnOs({ LINUX, MAC }) void onLinuxOrMac() { // ... } @Test @DisabledOnOs(WINDOWS) void notOnWindows() { // ... } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Test @EnabledOnOs(MAC) @interface TestOnMac { }
JRE Conditions
@EnabledOnJre
and @DisabledOnJre
are used to specify the version, @EnabledForJreRange
and @DisabledForJreRange
are used to specify the version range, example :
@Test @EnabledOnJre(JAVA_8) void onlyOnJava8() { // ... } @Test @EnabledOnJre({ JAVA_9, JAVA_10 }) void onJava9Or10() { // ... } @Test @EnabledForJreRange(min = JAVA_9, max = JAVA_11) void fromJava9to11() { // ... } @Test @EnabledForJreRange(min = JAVA_9) void fromJava9toCurrentJavaFeatureNumber() { // ... } @Test @EnabledForJreRange(max = JAVA_11) void fromJava8To11() { // ... } @Test @DisabledOnJre(JAVA_9) void notOnJava9() { // ... } @Test @DisabledForJreRange(min = JAVA_9, max = JAVA_11) void notFromJava9to11() { // ... } @Test @DisabledForJreRange(min = JAVA_9) void notFromJava9toCurrentJavaFeatureNumber() { // ... } @Test @DisabledForJreRange(max = JAVA_11) void notFromJava8to11() { // ... }
JVM system property conditions
@EnabledIfSystemProperty
and @DisabledIfSystemProperty
, examples:
@Test @EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*") void onlyOn64BitArchitectures() { // ... } @Test @DisabledIfSystemProperty(named = "ci-server", matches = "true") void notOnCiServer() { // ... }
Environment variable conditions
@EnabledIfEnvironmentVariable
and @DisabledIfEnvironmentVariable
, examples:
@Test @EnabledIfEnvironmentVariable(named = "ENV", matches = "staging-server") void onlyOnStagingServer() { // ... } @Test @DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*") void notOnDeveloperWorkstation() { // ... }
Now I have also found a lot of test friends and created a communication group to share technology, sharing a lot of technical documents and video tutorials we collected. If you don’t want to experience the feeling of not being able to find resources when studying on your own, having no one to answer your questions, and persisting for a few days before giving up. You can join us to communicate. And there are many technical experts who have made certain achievements in automation, performance, security, test development, etc. Share their experience, and also share many live lectures and technical salons You can learn for free! Focus on it! Open source! ! ! QQ group number: 110685036
Nested Test
Nested tests help us layer our test structure. With the help of Java nested class syntax, JUnit5 can implement nested testing through the @Nested
annotation. Example:
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.EmptyStackException; import java.util.Stack; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @DisplayName("A stack") class TestingAStackDemo { Stack<Object> stack; @Test @DisplayName("is instantiated with new Stack()") void isInstantiatedWithNew() { new Stack<>(); } @Nested @DisplayName("when new") class WhenNew { @BeforeEach void createNewStack() { stack = new Stack<>(); } @Test @DisplayName("is empty") void isEmpty() { assertTrue(stack.isEmpty()); } @Test @DisplayName("throws EmptyStackException when popped") void throwsExceptionWhenPopped() { assertThrows(EmptyStackException.class, stack::pop); } @Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { assertThrows(EmptyStackException.class, stack::peek); } @Nested @DisplayName("after pushing an element") class AfterPushing { String anElement = "an element"; @BeforeEach void pushAnElement() { stack.push(anElement); } @Test @DisplayName("it is no longer empty") void isNotEmpty() { assertFalse(stack.isEmpty()); } @Test @DisplayName("returns the element when popped and is empty") void returnElementWhenPopped() { assertEquals(anElement, stack.pop()); assertTrue(stack.isEmpty()); } @Test @DisplayName("returns the element when peeked but remains not empty") void returnElementWhenPeeked() { assertEquals(anElement, stack.peek()); assertFalse(stack.isEmpty()); } } } }
The external test class passes variables to the internal test class through @BeforeEach
.
Result after execution:
Repeat test
The @RepeatedTest
annotation can control the number of repeated executions of the test method, example:
import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.logging.Logger; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.RepetitionInfo; import org.junit.jupiter.api.TestInfo; class RepeatedTestsDemo { private Logger logger = // ... @BeforeEach void beforeEach(TestInfo testInfo, RepetitionInfo repetitionInfo) { int currentRepetition = repetitionInfo.getCurrentRepetition(); int totalRepetitions = repetitionInfo.getTotalRepetitions(); String methodName = testInfo.getTestMethod().get().getName(); logger.info(String.format("About to execute repetition %d of %d for %s", // currentRepetition, totalRepetitions, methodName)); } @RepeatedTest(10) void repeatedTest() { // ... } @RepeatedTest(5) void repeatedTestWithRepetitionInfo(RepetitionInfo repetitionInfo) { assertEquals(5, repetitionInfo.getTotalRepetitions()); } @RepeatedTest(value = 1, name = "{displayName} {currentRepetition}/{totalRepetitions}") @DisplayName("Repeat!") void customDisplayName(TestInfo testInfo) { assertEquals("Repeat! 1/1", testInfo.getDisplayName()); } @RepeatedTest(value = 1, name = RepeatedTest.LONG_DISPLAY_NAME) @DisplayName("Details...") void customDisplayNameWithLongPattern(TestInfo testInfo) { assertEquals("Details... :: repetition 1 of 1", testInfo.getDisplayName()); } @RepeatedTest(value = 5, name = "Wiederholung {currentRepetition} von {totalRepetitions}") void repeatedTestInGerman() { // ... } }
Among them, name can be used to customize the display name of repeated tests. {currentRepetition}
and {totalRepetitions}
are variables for the current number and total number of times.
Results of the:
├─ RepeatedTestsDemo? │ ├─ repeatedTest() ? │ │ ├─ repetition 1 of 10 ? │ │ ├─ repetition 2 of 10 ? │ │ ├─ repetition 3 of 10 ? │ │ ├─ repetition 4 of 10 ? │ │ ├─ repetition 5 of 10 ? │ │ ├─ repetition 6 of 10 ? │ │ ├─ repetition 7 of 10 ? │ │ ├─ repetition 8 of 10 ? │ │ ├─ repetition 9 of 10 ? │ │ └─ repetition 10 of 10 ? │ ├─ repeatedTestWithRepetitionInfo(RepetitionInfo) ? │ │ ├─ repetition 1 of 5 ? │ │ ├─ repetition 2 of 5 ? │ │ ├─ repetition 3 of 5 ? │ │ ├─ repetition 4 of 5 ? │ │ └─ repetition 5 of 5 ? │ ├─ Repeat! ? │ │ └─ Repeat! 1/1 ? │ ├─ Details... ? │ │ └─ Details... :: repetition 1 of 1 ? │ └─ repeatedTestInGerman() ? │ ├─ Wiederholung 1 von 5 ? │ ├─ Wiederholung 2 von 5 ? │ ├─ Wiederholung 3 von 5 ? │ ├─ Wiederholung 4 von 5 ? │ └─ Wiederholung 5 von 5 ?
Summary
This article introduces the conditional testing, nested testing, and repeated testing of JUnit5 respectively, which can make testing more flexible and hierarchical. In addition to these, JUnit5 also supports another important and common test: parameterized testing.
Finally, I would like to thank everyone who has read my article carefully. Looking at the increase in fans and attention, there is always some courtesy. Although it is not a very valuable thing, if you can use it, you can take it directly!
Software testing interview document
We must study to find a high-paying job. The following interview questions are from the latest interview materials from first-tier Internet companies such as Alibaba, Tencent, Byte, etc., and some Byte bosses have given authoritative answers. After finishing this set I believe everyone can find a satisfactory job based on the interview information.