Tag Archives: Java

Java Collections Framework: Choosing the Right Data Structure

Java ships with a rich library of data structures under the java.util package. The problem is not finding a collection u2014 it is picking the right one. Using an ArrayList when you need fast membership checks, or a HashMap when you need sorted keys, is the kind of mismatch that shows up in code reviews and performance profiles.

This guide maps every important class to the use case it is designed for, explains the time complexity of its key operations, and provides working code you can copy directly into a project. By the end you will have an intuition for which collection to reach for in any situation.

Continue reading Java Collections Framework: Choosing the Right Data Structure

Java Streams API: The Complete Reference Guide

The Streams API, introduced in Java 8, fundamentally changed how Java developers process collections. Instead of writing loops that describe how to iterate, you write pipelines that describe what to compute. The result is code that is shorter, easier to parallelise, and far more composable than the equivalent imperative loop.

This guide is designed as a reference you will return to. Every method in the API is covered with a working example and annotated output. The final sections address collectors in depth, parallel streams, and the mistakes that trip up developers who are new to the functional style.

Continue reading Java Streams API: The Complete Reference Guide

10 AI Prompts to Debug and Fix JUnit 6 Test Failures

A failing test is either a genuine bug in your code or a problem with the test itself. A flaky test is worse — it destroys trust in the entire suite. Debugging test failures efficiently requires the right mental model, the right tools, and — increasingly — the right AI prompt to help you diagnose what went wrong and produce a targeted fix.

This post gives you 10 AI prompts engineered specifically for debugging and fixing broken, flaky, or incorrectly behaving JUnit 6 tests. Each prompt is designed to give the AI enough context to diagnose the root cause and produce a corrected test — not just guess at a fix.

For complementary reading, see Debugging JUnit 6 Tests: Fix Failures Like a Pro and How to Fix Flaky Tests in JUnit 6.

Continue reading 10 AI Prompts to Debug and Fix JUnit 6 Test Failures

10 AI Prompts to Optimise and Update Existing JUnit 6 Tests

Existing test suites accumulate technical debt just like production code. Tests that were written quickly become brittle, slow, hard to read, or duplicated. Optimising and updating your JUnit 6 tests is not just housekeeping — it directly determines whether your test suite remains a trusted safety net or becomes a maintenance burden that developers work around.

This post gives you 10 targeted AI prompts designed to refactor, improve, and modernise existing JUnit 6 test code. Each prompt addresses a specific quality problem — from brittle mocking to slow Spring contexts to missing boundary cases — and produces immediately usable improved test code.

For foundational context, see Writing Maintainable Tests in JUnit 6 and Refactoring Legacy Tests to JUnit 6.

Continue reading 10 AI Prompts to Optimise and Update Existing JUnit 6 Tests

10 AI Prompts to Generate JUnit 6 Tests for New Projects

One of the most powerful applications of AI coding assistants is generating JUnit 6 test cases from your production code. But the quality of the output depends almost entirely on the quality of your prompt. A vague prompt produces vague tests. A precise, context-rich prompt produces tests that are immediately useful, well-structured, and actually cover the scenarios that matter.

This post gives you 10 battle-tested AI prompts you can copy, adapt, and use immediately with GitHub Copilot, Claude, ChatGPT, JetBrains AI, or any LLM to generate JUnit 6 tests for new Java projects. Each prompt is explained so you understand what it produces and when to use it.

For background on JUnit 6 test structure, see the JUnit 6 Tutorial Series. For best practices on what makes a test maintainable, see Writing Your First Clean Test in JUnit 6.

Continue reading 10 AI Prompts to Generate JUnit 6 Tests for New Projects

JUnit 6 vs Spock vs TestNG: Best Testing Framework for Java?

If you are choosing a testing framework for a new Java project — or evaluating whether to migrate an existing one — you need an honest, detailed comparison of all three major options. JUnit 6, Spock, and TestNG each have genuine strengths and real trade-offs. This guide compares them across every dimension that matters: syntax, features, performance, ecosystem, and team fit.

Framework Profiles

Before diving into code, it helps to understand each framework at a high level. JUnit 6 is the direct evolution of the most widely used Java testing library in history, built on a clean three-module architecture. Spock takes a fundamentally different approach — it is written in Groovy and embraces a specification-style, BDD-inspired syntax that many developers find more expressive than annotation-driven frameworks. TestNG was created to address limitations in early JUnit versions and remains popular for its advanced grouping, parallel execution, and test dependency features. The table below captures the most important dimensions at a glance before we explore each in depth.

JUnit 6SpockTestNG
LanguageJavaGroovy (runs on JVM)Java
First release1997 (JUnit 1)20082004
ParadigmAnnotation-drivenSpecification-based (BDD)Annotation-driven
MockingSeparate (Mockito)Built-in (Spock Mocks)Separate (Mockito)
Spring Boot supportNative, first-classVia spock-springManual config
Learning curveLow (Java devs)Medium (need Groovy)Low (Java devs)

Syntax Comparison: The Same Test in All Three

The most revealing comparison is seeing exactly the same test case written in each framework side by side. All three examples below test the same behaviour: placing a valid order should save it via the repository and return an order with CONFIRMED status. Pay attention to how each framework handles the test structure, mock setup, and verification — the differences in verbosity and readability are immediately apparent and reflect each framework’s core design philosophy.

Continue reading JUnit 6 vs Spock vs TestNG: Best Testing Framework for Java?

JUnit 6 vs Mockito: Roles, Differences, and Integration

If you are new to Java testing, you might wonder: are JUnit 6 and Mockito competitors? Do I need both? When does Mockito replace JUnit 6? The answer is that they are not alternatives — they are complementary tools with completely different jobs. JUnit 6 runs your tests. Mockito creates the fake objects your tests use. This guide clarifies their distinct roles, shows how they integrate, and answers every question developers have about which tool does what.

What JUnit 6 Does

JUnit 6 is a test runner and test framework. Its responsibilities are:

  • Discovering test classes and test methods on the classpath
  • Managing the test lifecycle (@BeforeEach, @AfterEach, etc.)
  • Executing test methods and reporting pass/fail/skip
  • Providing assertion methods (assertEquals, assertThrows, assertAll)
  • Supporting parameterized tests, dynamic tests, and parallel execution
  • Integrating with build tools (Maven, Gradle) and IDEs

What Mockito Does

Mockito is a mocking framework. Its responsibilities are:

  • Creating fake (mock) implementations of interfaces and classes
  • Defining return values for method calls on mocks (stubbing)
  • Verifying that methods were called with expected arguments
  • Creating spies that wrap real objects with selective overrides
  • Capturing method arguments for detailed inspection

Mockito does NOT run tests. JUnit 6 does NOT create mocks. They work together.

The Division of Labour

// This test uses BOTH JUnit 6 and Mockito — each doing its own job

import org.junit.jupiter.api.*;          // JUnit 6: @Test, @BeforeEach, @DisplayName
import org.mockito.*;                    // Mockito: @Mock, @InjectMocks
import org.mockito.junit.jupiter.*;      // Bridge: connects the two
import static org.junit.jupiter.api.Assertions.*; // JUnit 6: assertions
import static org.mockito.Mockito.*;     // Mockito: when(), verify()

// JUnit 6 discovers and runs this class
@ExtendWith(MockitoExtension.class)  // Mockito registers as a JUnit 6 extension
class OrderServiceTest {

    // ---- Mockito's job: create fake dependencies ----
    @Mock
    private OrderRepository orderRepository; // fake repository

    @Mock
    private EmailService emailService;       // fake email service

    @InjectMocks
    private OrderService orderService;       // real class under test

    // ---- JUnit 6's job: lifecycle management ----
    @BeforeEach
    void printTestStart() {
        System.out.println("Test starting...");
    }

    // ---- JUnit 6's job: discover and execute this test ----
    @Test
    @DisplayName("Placing an order persists it and sends a confirmation email")
    void placingOrderPersistsItAndSendsEmail() {

        // ---- Mockito's job: define stub behaviour ----
        Order savedOrder = new Order(1L, "[email protected]", 99.99, OrderStatus.CONFIRMED);
        when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);

        // ---- The actual call under test ----
        Order result = orderService.placeOrder("[email protected]", 99.99);

        // ---- JUnit 6's job: assert the result ----
        assertNotNull(result.getId(),              "Order ID must be assigned");
        assertEquals(OrderStatus.CONFIRMED,        result.getStatus());

        // ---- Mockito's job: verify interactions ----
        verify(orderRepository, times(1)).save(any(Order.class));
        verify(emailService, times(1)).sendConfirmation("[email protected]");
    }
}
Continue reading JUnit 6 vs Mockito: Roles, Differences, and Integration