Lecture 4_5
Lecture 4_5
Lecture 4_5
Lecture 4
Unit test with java
Outline
• Unit testing and test case
• JUnit
• Mockito
References
System
Testing levels
Why unit test?
• Unit test case is composed of 4 stages. To control these test phases, Junit
5 using different annotations.
• Mapping the annotations to control the test lifecycle with the different
parts of a test case:
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class LifecycleJUnit5Test { @Test
@BeforeAll void testTwo() {
static void setupAll() { System.out.println("TEST 2");
System.out.println("Setup ALL }
TESTS in the class"); @AfterEach
} void teardown() {
@BeforeEach System.out.println("Teardown
void setup() { EACH TEST in the class");
System.out.println("Setup EACH }
TEST in the class"); @AfterAll
} static void teardownAll() {
@Test System.out.println("Teardown
void testOne() { ALL TESTS in the class");
System.out.println("TEST 1"); }
} }
Annotations
• All JUnit Jupiter assertions are static methods in the Assertions class
located in org.junit.jupiter package.
Assertion Description
fail Fails a test with a given message and/or exception
assertTrue Asserts that a supplied condition is true
assertFalse Asserts that a supplied condition is false
assertNull Asserts that a supplied object is null
assertNotNull Asserts that a supplied object is not null
assertEquals Asserts that two supplied objects are equal
assertArrayEquals Asserts that two supplied arrays are equal
assertIterableEquals Asserts that two iterable objects are deeply equal
assertLinesMatch Asserts that two lists of Strings are equals
assertNotEquals Asserts that two supplied objects are not equal
assertSame Asserts that two objects are the same, compared with ==
assertNotSame Asserts that two objects are different, compared with !=
Special assertions
Assertion Description
assertAll Group of assertions: this method allows to group different
assertions at the same time. In a grouped assertion, all
assertions are always executed, and any failures will be
reported together
assertThrows Asserting exceptions: this assertion allows to verify if a given
exception is raised in a piece of code.
assertTimeout Asserting timeouts: this assertion allows us to verify the
timeout of a given operation.
assertTimeoutPreemtively Asserting timeouts: this assertion allows us to verify the
timeout of a given operation. The difference with
assertTimeoutPreemptively with respect to
assertTimeout is that
assertTimeoutPreemptively does not wait until the
end of the operation, and the execution is aborted when the
expected timeout is exceeded
Example
class StandardAssertionsTest {
@Test
void standardAssertions() {
assertEquals(2, 2);
assertTrue(true, "The optional assertion message is now the
last parameter");
assertFalse(false, () -> "Really " + "expensive " +
"message" + ".");
}
}
class GroupedAssertionsTest {
@Test
void groupedAssertions() {
Address address = new Address("John", "Smith");
assertAll("address", () -> assertEquals("John",
address.getFirstName()), () -> assertEquals("User",
address.getLastName()));
}
}
Example
class ExceptionTest {
@Test
void exceptionTesting() {
Throwable exception =
assertThrows(IllegalArgumentException.class, () -> { throw new
IllegalArgumentException("a message");});
assertEquals("a message", exception.getMessage());
}
}
class TimeoutExceededTest {
@Test
void timeoutNotExceeded() {
assertTimeout(ofMinutes(2), () -> {
Thread.sleep(100); });
}
@Test
void timeoutExceeded() {
assertTimeout(ofMillis(10), () -> {
Thread.sleep(100); });
}
}
Example
class TimeoutWithResultOrMethodTest {
@Test
void timeoutNotExceededWithResult() {
String actualResult = assertTimeout(ofMinutes(1), () -> {
return "hi there"; });
assertEquals("hi there", actualResult);
}
@Test
void timeoutNotExceededWithMethod() {
String actualGreeting = assertTimeout(ofMinutes(1),
TimeoutWithResultOrMethodTest::greeting);
assertEquals("hello world!", actualGreeting);
}
private static String greeting() { return "hello world!"; }
}
class TimeoutWithPreemptiveTerminationTest {
@Test void timeoutExceededWithPreemptiveTermination() {
assertTimeoutPreemptively(ofMillis(10), () -> {
Thread.sleep(100); });
}
}
Advanced JUnit Features
class DynamicExampleTest {
@TestFactory
Stream<DynamicTest> dynamicTestsFromStream() {
Stream<String> inputStream = Stream.of("A", "B", "C");
return inputStream.map( input -> dynamicTest("Display name
for input " + input, () -> { System.out.println("Testing " +
input); })); }
}
Example
class StreamExampleTest {
@TestFactory
Stream<DynamicTest> streamTest() {
// Input data
Integer array[] = { 1, 2, 3 };
Iterator<Integer> inputGenerator =
Arrays.asList(array).iterator();
// Display names
Function<Integer, String> displayNameGenerator = ( input) -
> "Data input:" + input;
// Test executor
ThrowingConsumer<Integer> testExecutor = (input) -> {
System.out.println(input); assertTrue(input % 2 == 0); };
• Parameterized tests are a special kinds of tests in which the data input is
injected in the test in order to reuse the same test logic.
• Steps to create parameterized tests:
• We need to use the annotation @ParameterizedTest (located in the
package org.junit.jupiter.params) to declare a method within a
Java class as a parameterized test.
• We need to specify at least one argument provider.
Parameterized Tests
Arguments provider Description
annotation
@ValueSource Used to specify an array of literal values of String, int, long, or double
@EnumSource Argument source for constants of a specified enumeration
(java.lang.Enum)
@MethodSource Provides access to values returned by static methods of the class in
which this annotation is declared
@CsvSource Argument source which reads comma-separated values (CSV) from its
attribute
@CsvFileSource Argument source which is used to load CSV files from one or more
classpath resources
@ArgumentSource Used to specify a custom argument provider (that is, a Java class that
implements the interface)
org.junit.jupiter.params.provider.ArgumentsProvider)
@ValueSource
class ValueSourceStringsParameterizedTest {
@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void testWithStrings(String argument) {
System.out.println("Parameterized test with (String)
parameter: " + argument);
assertNotNull(argument);
}
}
@EnumSource
class EnumSourceParameterizedTest {
@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnum(TimeUnit argument) {
System.out.println("Parameterized test with (TimeUnit)
argument: " + argument);
assertNotNull(argument); }
}
@MethodSource
class MethodSourceStringsParameterizedTest {
static Stream stringProvider() {
return Stream.of("hello", "world"); }
@ParameterizedTest
@MethodSource("stringProvider")
void testWithStringProvider(String argument) {
System.out.println("Parameterized test with (String)
argument: " + argument);
assertNotNull(argument);
}
}
@CsvSource and @CsvFileSource
class ArgumentSourceParameterizedTest {
@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider1.class)
void testWithArgumentsSource(String first, int second) {
System.out.println("Parameterized test with (String) " +
first + " and (int) " + second);
assertNotNull(first);
assertTrue(second > 0); }
}
@ArgumentsSource
import java.util.stream.Stream;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
@Override
public Stream<? extends Arguments> provideArguments(
ExtensionContext context) {
System.out.println("Arguments provider to test " +
context.getTestMethod().get().getName());
return Stream.of(Arguments.of("hello", 1),
Arguments.of("world", 2));
}
}
Mockito
@Mock
MyDoc docMock;
• The differential aspect of mocks object with respect to other test doubles
(such as stub) is that mock objects can be programmed with custom
expectations according to the needs of the unit test.
• This process in the Mockito is known as stubbing methods, in which
these methods belong to the mocks.
Mockito API Description
Mockito.when(x).thenReturn(y) These methods allow us to specify the value (y)
Mockito.doReturn(y).when(x) that should be returned by the stubbed method
(x) of a given mock object.
Mockito.when(x).thenThrow(e) These methods allow us to specify the exception
Mockito.doThrow(e).when(x) (e) that should be thrown when calling a stubbed
method (x) of a given mock object.
Mockito.when(x).thenAnswer(a) Unlike returning a hardcoded value, a dynamic
Mockito.doAnswer(a).when(x) user defined logic (Answer a) is executed when a
given method (x) of the mock is invoked.
Mockito.when(x).thenCallRealMethod() This method allows us the real implementation of
Mockito.doCallRealMethod().when(x) a method instead the mocked one.
Verification
@Mock
LoginService loginService;
// Test data
UserForm userForm = new UserForm("foo", "bar");
@Test
void testLoginOk() {
// Setting expectations (stubbing methods)
when(loginService.login(userForm)).thenReturn(true);
// Exercise SUT
String reseponseLogin = loginController.login(userForm);
// Verification
assertEquals("OK", reseponseLogin);
verify(loginService).login(userForm);
verifyNoMoreInteractions(loginService);
}
}
Notes
• Test case
• JUnit
• Mockito