Unit Testing – Test Doubles: Dummy, Stub and Mock Examples

// Library.java
public class Library {
    private final LibraryRepository libraryRepository;
    private final NotificationService notificationService;
    private final LoggerService loggerService;

    public Library(LibraryRepository libraryRepository, NotificationService notificationService, LoggerService loggerService) {
        this.libraryRepository = libraryRepository;
        this.notificationService = notificationService;
        this.loggerService = loggerService;
    }

    public boolean addBook(String bookId, String title) {
        if (libraryRepository.bookExists(bookId)) {
            loggerService.log("Book already exists: " + bookId);
            return false;
        }
        libraryRepository.addBook(bookId, title);
        loggerService.log("Book added: " + title);
        notificationService.notifyAddBook(title);
        return true;
    }
}

// LibraryRepository.java
public class LibraryRepository {
    private final Database database;

    public LibraryRepository(Database database) {
        this.database = database;
    }

    public boolean bookExists(String bookId) {
        return database.contains(bookId);
    }

    public void addBook(String bookId, String title) {
        database.save(bookId, title);
    }
}

// NotificationService.java
public class NotificationService {
    private final EmailService emailService;
    private final SMSService smsService;

    public NotificationService(EmailService emailService, SMSService smsService) {
        this.emailService = emailService;
        this.smsService = smsService;
    }

    public void notifyAddBook(String title) {
        emailService.sendEmail("A new book has been added: " + title);
        smsService.sendSMS("A new book has been added: " + title);
    }
}

// LoggerService.java
public class LoggerService {
    public void log(String message) {
        System.out.println(message);
    }
}

// Database.java
public class Database {
    public boolean contains(String key) {
        // Dummy implementation
        return false;
    }

    public void save(String key, String value) {
        // Dummy implementation
    }
}

// EmailService.java
public class EmailService {
    public void sendEmail(String message) {
        // Dummy implementation
    }
}

// SMSService.java
public class SMSService {
    public void sendSMS(String message) {
        // Dummy implementation
    }
}

// LibraryTest.java
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

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

public class LibraryTest {

    @Test
    public void testAddBookWithDummy() {
        Database dummyDatabase = new Database();
        LibraryRepository libraryRepository = new LibraryRepository(dummyDatabase);
        EmailService dummyEmailService = new EmailService();
        SMSService dummySmsService = new SMSService();
        NotificationService notificationService = new NotificationService(dummyEmailService, dummySmsService);
        LoggerService dummyLoggerService = new LoggerService();

        Library library = new Library(libraryRepository, notificationService, dummyLoggerService);
        boolean result = library.addBook("1", "Dummy Book");

        // Assertion to verify the result
        assertTrue(result, "The book should be added successfully since the dummy database does not contain it.");
    }

    @Test
    public void testAddBookWithStub() {
        Database stubDatabase = mock(Database.class);
        when(stubDatabase.contains("1")).thenReturn(false);
        LibraryRepository libraryRepository = new LibraryRepository(stubDatabase);

        NotificationService notificationService = mock(NotificationService.class);
        LoggerService loggerService = mock(LoggerService.class);

        Library library = new Library(libraryRepository, notificationService, loggerService);
        boolean result = library.addBook("1", "Stub Book");

        assertTrue(result);
        verify(loggerService).log("Book added: Stub Book");
        verify(notificationService).notifyAddBook("Stub Book");
    }

    @Test
    public void testAddBookWithMock() {
        LibraryRepository mockRepository = mock(LibraryRepository.class);
        NotificationService mockNotificationService = mock(NotificationService.class);
        LoggerService mockLoggerService = mock(LoggerService.class);

        when(mockRepository.bookExists("1")).thenReturn(false);

        Library library = new Library(mockRepository, mockNotificationService, mockLoggerService);
        boolean result = library.addBook("1", "Mock Book");

        assertTrue(result);
        verify(mockRepository).addBook("1", "Mock Book");
        verify(mockLoggerService).log("Book added: Mock Book");
        verify(mockNotificationService).notifyAddBook("Mock Book");
    }
}