Labs ICT
โญ Pro Login

Testing Best Practices

Writing tests is one thing. Writing good tests is another. These best practices come from years of experience across the Java testing community. Following them will save you from a lot of pain down the road.

1. Follow the FIRST Principles


+---------+--------------------------------------------------+
| Principle | Meaning                                        |
+---------+--------------------------------------------------+
| F       | Fast - Tests should run quickly                  |
| I       | Independent - Tests should not depend on each    |
|         | other                                            |
| R       | Repeatable - Same result every time              |
| S       | Self-validating - Pass or fail, no manual check  |
| T       | Timely - Write tests at the right time           |
+---------+--------------------------------------------------+
    

// BAD: Test depends on another test
@Test
void test1() {
    sharedState = "value";
}

@Test
void test2() {
    // Depends on test1 setting sharedState!
    assertEquals("value", sharedState);
}

// GOOD: Each test is independent
@Test
void test2() {
    String state = setupState();
    assertEquals("value", state);
}
    

2. Use Descriptive Names


// BAD
@Test
void test1() { }

@Test
void testAdd() { }

// GOOD
@Test
@DisplayName("should return sum when adding two positive numbers")
void addPositiveNumbers() { }

@Test
@DisplayName("should throw exception when dividing by zero")
void divideByZero() { }
    

3. One Assertion Per Test


// BAD: Testing too many things
@Test
void testUser() {
    assertEquals("John", user.getName());
    assertEquals("john@example.com", user.getEmail());
    assertTrue(user.isActive());
    assertEquals(25, user.getAge());
}

// GOOD: Focused tests
@Test
@DisplayName("should return user name")
void userName() {
    assertEquals("John", user.getName());
}

@Test
@DisplayName("should return user email")
void userEmail() {
    assertEquals("john@example.com", user.getEmail());
}
    

4. Arrange-Act-Assert (AAA)


@Test
void shouldCalculateDiscount() {
    // Arrange
    Customer customer = new Customer("premium");
    Order order = new Order(100.00);

    // Act
    double discount = discountService.calculate(customer, order);

    // Assert
    assertEquals(20.00, discount);
}
    

5. Test Edge Cases


class StringHelperTest {

    @Test
    void normalInput() {
        assertEquals("olleh", helper.reverse("hello"));
    }

    @Test
    void emptyString() {
        assertEquals("", helper.reverse(""));
    }

    @Test
    void singleCharacter() {
        assertEquals("a", helper.reverse("a"));
    }

    @Test
    void palindrome() {
        assertEquals("racecar", helper.reverse("racecar"));
    }

    @Test
    void nullInput() {
        assertThrows(NullPointerException.class,
            () -> helper.reverse(null));
    }

    @Test
    void veryLongString() {
        String longString = "a".repeat(10000);
        assertEquals(longString, helper.reverse(longString));
    }
}
    

6. Avoid Magic Numbers


// BAD
assertEquals(86400, session.getTimeout());

// GOOD
int ONE_DAY_IN_SECONDS = 86400;
assertEquals(ONE_DAY_IN_SECONDS, session.getTimeout());
    

7. Keep Tests Fast

  • Use in-memory databases instead of real ones
  • Mock external services
  • Avoid Thread.sleep() โ€” use Awaitility instead
  • Minimize I/O operations

8. Use @Nested for Readability


class OrderServiceTest {

    @Nested
    class WhenOrderIsValid {
        @Test
        void shouldProcessOrder() { }
        @Test
        void shouldSendConfirmation() { }
    }

    @Nested
    class WhenOrderIsInvalid {
        @Test
        void shouldRejectOrder() { }
        @Test
        void shouldNotifyUser() { }
    }
}
    

9. Test Behavior, Not Implementation


// BAD: Tests implementation details
@Test
void shouldCallInternalMethod() {
    service.process();
    verify(service).internalStep1();
    verify(service).internalStep2();
}

// GOOD: Tests behavior
@Test
void shouldProcessOrder() {
    Order order = new Order(items);
    Result result = service.process(order);
    assertEquals(Status.SUCCESS, result.getStatus());
    assertNotNull(result.getOrderId());
}
    

10. Review Tests Like Code

Tests are code. They deserve the same attention to quality:

  • Code review your tests
  • Refactor tests that become hard to understand
  • Keep test code as clean as production code
  • Delete tests that no longer add value

๐Ÿงช Quick Quiz

Which of these is a testing best practice?