0
0
JUnittesting~5 mins

Why different doubles serve different purposes in JUnit

Choose your learning style9 modes available
Introduction

Test doubles help us replace real parts in tests to check behavior safely and easily.

When you want to check if a method was called without running real code.
When the real part is slow or not ready yet.
When you want to control what a part returns to test different cases.
When you want to isolate the part you are testing from others.
When you want to simulate errors or special situations.
Syntax
JUnit
public interface Service {
    String getData();
}

// Stub example
class StubService implements Service {
    public String getData() {
        return "stub data";
    }
}

// Mock example using Mockito
Service mockService = Mockito.mock(Service.class);
Mockito.when(mockService.getData()).thenReturn("mock data");

// Spy example using Mockito
Service realService = new RealService();
Service spyService = Mockito.spy(realService);
Mockito.when(spyService.getData()).thenReturn("spy data");

Test doubles include stubs, mocks, spies, and fakes.

Mockito is a popular library to create mocks and spies in JUnit tests.

Examples
A stub returns fixed data to help test without real logic.
JUnit
class StubService implements Service {
    public String getData() {
        return "stub data";
    }
}
A mock can verify interactions and return controlled data.
JUnit
Service mockService = Mockito.mock(Service.class);
Mockito.when(mockService.getData()).thenReturn("mock data");
A spy wraps a real object but can override some behavior.
JUnit
Service realService = new RealService();
Service spyService = Mockito.spy(realService);
Mockito.when(spyService.getData()).thenReturn("spy data");
Sample Program

This test class shows stub, mock, and spy usage with JUnit and Mockito.

JUnit
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

interface Service {
    String getData();
}

class RealService implements Service {
    public String getData() {
        return "real data";
    }
}

public class DoubleTest {

    @Test
    void testStub() {
        Service stub = new Service() {
            public String getData() {
                return "stub data";
            }
        };
        assertEquals("stub data", stub.getData());
    }

    @Test
    void testMock() {
        Service mock = Mockito.mock(Service.class);
        Mockito.when(mock.getData()).thenReturn("mock data");
        assertEquals("mock data", mock.getData());
        Mockito.verify(mock).getData();
    }

    @Test
    void testSpy() {
        RealService real = new RealService();
        Service spy = Mockito.spy(real);
        Mockito.when(spy.getData()).thenReturn("spy data");
        assertEquals("spy data", spy.getData());
        Mockito.verify(spy).getData();
    }
}
OutputSuccess
Important Notes

Use stubs to provide simple fixed responses.

Use mocks to check if methods were called and control returns.

Use spies to wrap real objects but override some behavior.

Summary

Different doubles help test parts safely and clearly.

Stubs return fixed data, mocks verify calls, spies wrap real objects.

Choosing the right double makes tests easier and more reliable.