JUnit is a general test framework for unit tests, integration tests, Test-Driven Development (TDD) etc. This is one of the most commonly used test environment.
The IntelliJ IDEA IDE - when creating a new projects - asks which test framework should be used and will automatically handle the dependency in the background. So, it is not needed to add dependency to the pom.xml.
JUnit extends the functionality of Java core assertions. Furthermore, works together with IntelliJ IDEA perfectly. As a result, a method marked with @Test annotation will be handled as a test method and in case the IDE or Maven wants to run tests, will invoke these marked methods.
Naming conventions of test classes:
Test*
*Test
*Tests
*TestCase
By following these conventions, Maven and the IDE will identify test methods and make jumping to-from live and test codes possible.
When Maven or IntelliJ IDEA executes tests, invoking happens parallel by launching the methods on different threads.
Because JUnit will handle all methods, access modifiers should not be written, no arguments, and all return values should be void in method signatures.
If needed, normal helper methods can be written for the test methods, naturally.
Rule is that a test method must not call an other test method or its helper method since test methods should be independent and invoked parallel.
HTML test reports can be generated by launching under Maven the next command:
mvn clean verify surefire-report:report
Using any annotations is optional except @Test which is mandatory for test methods.
In case there is a method marked with @BeforeEach annotation, that method will be the starting method on all test threads, following by the specific test method.
Similarly, in case there is a method marked with @AfterEach, that method will be added to the end of every invoking chain on all test threads.
The @BeforeAll and @AfterAll methods are invoked once, before any test thread is started and after all threads reached their last specific statements. For this, because Spring creates instances for all test methods along with @BeforeEach and @AfterEach codes one-by-one, the @BeforeAll and @AfterAll methods must be static.
When the live code has been changed but the test has not been aligned yet, use @Disabled. Best practice is to mark the reason. See Assumptions for alternatives below.
EXAMPLE FOR JUNIT TEST UNIT:
JUnit provides a wide variety of possible expected versus actual values by providing the Assertions class.
The test program flow stops on the Assertions method where the condition represented by the method fails. In case the flow steps further, the test passed.
The checked condition is readable in the name of the Assertions method.
For 2-parameter methods, first parameter is always the expected, the second is the actual value or their containers.
For 1-parameter methods, the parameter is the actual value or its container.
All methods can have an additional parameter which contains a message String to be sent to the output in case of fail. If this parameter is missing, default text will be sent.
There are two main directions in how many Assertions a test method could contain:
one (a test method is created for every checked values)
many (a test method is created for testing a code scope or a strategy)
In case of Test-Driven Development or when the followed testing strategy needs it, a test method represents a test case. In such, if in the test the developer wants to check all critical points (and separate them from each other), will use several Assertions.
The next methods has different argument list.
Assertions methods return void except assertThrows() which returns an exception upon failing.
In case the tested object is an instance of a custom class, and the test should examine object value, the class must implement the equals() and hashCode() methods or else the test will be rendered down to general object equality problem (object is identical with the expected one or not etc.).
In case the tested values are in a List but the producer method uses parallel technique (e.g. Streams), the element order can not be predicted. Sorting the List before examining could be needed. For custom element types, implementing the compareTo() method (Comparable) or using a custom Comparator beforehand can be advised.
The Executable can be Runnable or Callable.
The assertEquals() and assertNotEquals() methods work on any class and primitive type for values.
In case of List, it is sensitive for element order. Use assertIterableEquals() (more verbal).
In case of Set, the element order is indifferent.
On primitive arrays, use the assertArrayEquals() method.
On boolean and Boolean values, it is better to use assertTrue() and assertFalse() methods (more verbal).
The assertIterableEquals() deeply compares (element number, element order, element) two Iterables (e.g. Lists).
The assertLinesMatch() is a specialized version of Assertion methods. Tests a List of Strings or a Stream of Strings against the expected.
The test follows the element order and uses three rules at all lines-pairs:
if expected.equals(actual) then the assertion passes for that line-pair, or
if expected.matches(actual) then the assertion passes for that line-pair, or
if expected line is a fast-forward marker then passes, or
fails
The assertion as whole passes when for all line-pairs it passes.
A fast-forward marker line starts with ">>" and ends with ">>" and contains at least four characters (e.g. ">>>>", ">> something message >>").
The assertThrows() and assertDoesNotThrow() methods are for testing for exceptions.
In case the thrown exception is a subclass of the expected, the test will pass.
When the expected exception is Exception, for any thrown exception the test will pass.
The assertThrows() method returns an exception. This exception can be examined e.g. for exception message after assertThrows().
The assertTimeout() and assertTimeoutPreemptively() methods are both used for examining running time of long-term codes.
The assertTimeoutPreemptively() method, once the expected duration is exceeded, will stop the tested Executable (Runnable or Callable), the assertTimeout() will not, the Executable must stop by itself.
In case of tested Callable, the return value can be examined afterwards.
Be aware, depending on the server which might be busy or not, the such test can pass or fail virtually randomly.
In a test method, the flow stops on the first assert fail.
If all asserts need to be examined, use assertAll():
assertAll(messageInCaseOfAnyFail, () -> assertMethod1(...), () -> assertMethod2(...), ...)
EXAMPLE FOR TESTED VARIABLES AND METHODS:
EXAMPLE FOR ASSERTEQUALS(), ASSERTNOTEQUALS(), ASSERTARRAYEQUALS(), ASSERTITERABLEEQUALS(), ASSERTLINESMATCH(), ASSERTTRUE(), ASSERTFALSE():
EXAMPLE FOR ASSERTSAME(), ASSERTNOTSAME(), ASSERTNULL(), ASSERTNOTNULL(), ASSERTINSTANCEOF():
EXAMPLE FOR ASSERTTHROWS() METHODS:
EXAMPLE FOR ASSERTTIMEOUT() AND ASSERTPREEMPTIVELYTIMEOUT() METHODS:
EXAMPLE FOR FAIL() METHOD:
EXAMPLE FOR ASSERTALL() METHOD:
Using an Assumption lets a test continue only when the condition defined with Assumption met.
In the background the Assumption generates TestAbortedException which will be translated by JUnit as skipped test.
An Assumption can appear in any line of a test method.
All Assertions before an Assumption will be evaluated but after that only with the given condition.
EXAMPLE FOR ASSUMPTIONS:
See @Disabled annotation for alternatives above.
For dynamic (test factory) and parameterized JUnit test variants, see the DYNAMIC AND PARAMETERIZED TESTS page.
A test method can have nested (inner) test classes.
Such classes:
must be marked with @Nested annotation
can have @BeforeEach and @AfterEach methods
can not have @BeforeAll and @AfterAll methods (since these optional methods already were invoked with the outer class)
must not be static
can have several test methods as a normal class would have
There is no limit in the hierarchic depth of nested test classes.
JUnit invokes the tests in deterministic but unpredictable order.
If strict order is needed by the developer, the class-level @TestMethodOrder annotation can be used together with @Order method-level annotation.
Custom method orderer can be used as well via the orderMethods() method, which allows calling context.getMethodDescriptors().sort(...)
EXAMPLE FOR USAGE OF @ORDER ANNOTATION:
Through the @TempDir argument-level annotation a method can use temporary Path and File objects.
When the test method is invoked, these are created via JUnit, and after testing, deleted automatically as well.
In case the test method is invoked repeatedly, JUnit will create and delete these temporary objects per cycle.
JUnit uses the ParameterResolutionException exception for control of this feature.
EXAMPLE FOR TEMPORARY FILES AND PATHS: