My simple library

..of useful code



Chapters

Java Unit Testing

1. Introduction to JUnit Testing

JUnit is a popular unit testing framework for Java applications. IntelliJ IDEA provides excellent support for creating, running, and debugging JUnit tests with features like:

  • Automatic test generation
  • Visual test runner
  • Code coverage analysis
  • Integration with build tools (Maven, Gradle)
  • Parameterized test support

2. Setting Up JUnit in IntelliJ IDEA

2.1 Adding JUnit Dependency

For Maven projects, add this to your pom.xml:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>

For Gradle projects, add this to your build.gradle:

testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'

Note:

IntelliJ IDEA comes with bundled JUnit libraries. You can also use these without adding dependencies by configuring the test framework in Project Structure (⌘; on Mac or Ctrl+Alt+Shift+S on Windows/Linux).

3. Creating Test Classes

3.1 Generating Tests Automatically

  1. Place the cursor on the class name in your source code
  2. Press ⌘N (Mac) or Alt+Insert (Windows/Linux)
  3. Select "Test..." from the menu
  4. In the dialog:
    • Choose testing library (JUnit 4 or JUnit 5)
    • Select methods to generate tests for
    • Choose destination package (usually same as source class but in test folder)

3.2 Manual Test Class Creation

Create a new class in your test directory (src/test/java) with naming convention:

OriginalClassName + "Test" (e.g., CalculatorTest)

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

    @Test
    void testAdd() {
        Calculator calculator = new Calculator();
        assertEquals(5, calculator.add(2, 3), "2 + 3 should be 5");
    }
}

4. Writing Test Methods

4.1 Basic Test Structure

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class StringUtilsTest {

    @Test
    void testIsEmpty() {
        assertTrue(StringUtils.isEmpty(""));
        assertFalse(StringUtils.isEmpty("text"));
    }

    @Test
    void testReverse() {
        assertEquals("cba", StringUtils.reverse("abc"));
        assertNull(StringUtils.reverse(null));
    }
}

4.2 Common Assertions

Assertion Description
assertEquals(expected, actual) Tests if two values are equal
assertTrue(condition) Tests if condition is true
assertFalse(condition) Tests if condition is false
assertNull(object) Tests if object is null
assertNotNull(object) Tests if object is not null
assertThrows(exception, executable) Tests if code throws expected exception

4.3 Test Lifecycle Annotations

Annotation Description
@BeforeEach Executed before each test method
@AfterEach Executed after each test method
@BeforeAll Executed once before all test methods
@AfterAll Executed once after all test methods
@DisplayName Custom display name for test class or method

5. Running Tests in IntelliJ

5.1 Running Individual Tests

  1. Click the green arrow icon next to the test class or method
  2. Right-click the test and select "Run"
  3. Use keyboard shortcut (Ctrl+Shift+F10 on Windows/Linux, ⌃⇧R on Mac)

5.2 Running All Tests

  • Right-click the test directory and select "Run 'All Tests'"
  • Use the Run configuration dropdown and select "All Tests"
  • Run from command line with Maven: mvn test or Gradle: gradle test

5.3 Debugging Tests

  1. Set breakpoints in your test or production code
  2. Click the debug icon (bug) instead of run
  3. Use keyboard shortcut (Shift+F9 on Windows/Linux, ⌃D on Mac)

6. Advanced Testing Techniques

6.1 Parameterized Tests

@ParameterizedTest
@ValueSource(ints = {1, 3, 5, -3, 15})
void testIsOdd(int number) {
    assertTrue(MathUtils.isOdd(number));
}

6.2 Mocking with Mockito

@Test
void testUserService() {
    // Create mock
    UserRepository mockRepo = Mockito.mock(UserRepository.class);

    // Define mock behavior
    when(mockRepo.findById(1L)).thenReturn(new User(1L, "test@example.com"));

    // Inject mock and test
    UserService service = new UserService(mockRepo);
    User user = service.getUserById(1L);

    assertEquals("test@example.com", user.getEmail());
    verify(mockRepo).findById(1L);
}

6.3 Testing Exceptions

@Test
void testWithdrawalFailsWhenBalanceInsufficient() {
    BankAccount account = new BankAccount(100);
    assertThrows(InsufficientFundsException.class, () -> {
        account.withdraw(200);
    });
}

7. JUnit Best Practices

  • Test Naming: Use descriptive names (testMethodName_StateUnderTest_ExpectedBehavior)
  • Single Responsibility: Each test should verify one thing
  • AAA Pattern: Arrange-Act-Assert structure
  • Independent Tests: Tests shouldn't depend on each other
  • Fast Tests: Keep tests fast (avoid I/O, use mocks)
  • Test Coverage: Aim for high coverage but focus on important paths

IntelliJ IDEA Tips:

  • Use Ctrl+Shift+T (Windows/Linux) or ⌘⇧T (Mac) to quickly navigate between test and production code
  • Enable "Coverage" when running tests to see which code is exercised
  • Use "Run with Coverage" to generate coverage reports
  • Configure test templates in Settings > Editor > Live Templates