BAWABAA
No Result
View All Result
  • Home
  • Products
  • Services
    • Courses
    • Pathways
  • Solutions
  • Plans
  • Resources
    • List of Curated Edtech Websites
    • Pathways
    • Blog
      • Programming Languages
      • Tech
      • Graphic Design
      • Inspiration
      • Software Testing
      • Operating Systems
      • Frameworks
BAWABAA
  • Home
  • Products
  • Services
    • Courses
    • Pathways
  • Solutions
  • Plans
  • Resources
    • List of Curated Edtech Websites
    • Pathways
    • Blog
      • Programming Languages
      • Tech
      • Graphic Design
      • Inspiration
      • Software Testing
      • Operating Systems
      • Frameworks
No Result
View All Result
BAWABAA
No Result
View All Result
Home Frameworks

Dummy Object Test Doubles in Unit testing with Java and Mockito

January 31, 2025
Reading Time: 22 mins read

A Dummy could resemble a stub rather than a pure dummy. Let’s clarify the distinction between dummy objects and stubs in unit testing and adjust the example to make it more fitting as a dummy if needed.


Difference Between a Dummy and a Stub:

  1. Dummy:
    • A dummy is an object that is passed into a method or constructor but is not used in the test.
    • It’s a placeholder to satisfy the method’s or class’s dependencies.
  2. Stub:
    • A stub is an object that provides predefined responses to method calls during a test.
    • It often includes minimal behavior needed to allow the test to run properly.

In the example above, the DummyDatabaseConnection provides a response ("Dummy User") to a method call (queryUserById). This behavior makes it a stub, as it actively participates in the logic of the test by returning a specific value.


How to Make It a True Dummy Example

To demonstrate a dummy object more clearly, we can modify the test so that the DatabaseConnection dependency is not used during the execution of the method being tested.

Adjusted Example with a Dummy:

Here, we’ll focus on a scenario where the dependency is present but irrelevant for the specific test.

Classes:
// Represents a real database connection
public class DatabaseConnection {
    public void connect() {
        System.out.println("Connecting to the database...");
    }

    public void disconnect() {
        System.out.println("Disconnecting from the database...");
    }

    public String queryUserById(int userId) {
        // This would typically perform a database query
        return "Real User"; // Placeholder for demonstration purposes
    }
}

// Service class that depends on DatabaseConnection
public class UserService {

    private DatabaseConnection databaseConnection;

    public UserService(DatabaseConnection databaseConnection) {
        this.databaseConnection = databaseConnection;
    }

    public String formatUserName(int userId) {
        // This method does not actually use the database
        return "User-" + userId;
    }
}

Dummy Implementation:

The dummy simply satisfies the dependency without being used.

// Dummy implementation with no real behavior
public class DummyDatabaseConnection extends DatabaseConnection {
    // No overridden methods because it won’t be called in this test
}

Test:

Here, the dummy object is passed into the UserService, but it is not actually used.

import org.junit.Test;
import static org.junit.Assert.*;

public class UserServiceTest {

    @Test
    public void testFormatUserName() {
        // Create a dummy database connection
        DatabaseConnection dummyConnection = new DummyDatabaseConnection();

        // Create the UserService instance with the dummy connection
        UserService userService = new UserService(dummyConnection);

        // Perform the test
        String formattedName = userService.formatUserName(42);

        // Validate the result
        assertEquals("User-42", formattedName);
    }
}

Key Points of the True Dummy Example:

  1. The Dependency Is Irrelevant:
    • DatabaseConnection is passed to the UserService constructor but is not used in the method being tested (formatUserName).
  2. No Active Behavior:
    • The dummy object does nothing—it is just there to satisfy the constructor’s requirements.
  3. Purpose of the Test:
    • The test focuses on the logic inside formatUserName, not the interaction with DatabaseConnection.

When to Use Dummy vs Stub:

  • Dummy: Use when the dependency isn’t directly involved in the test logic and serves only to satisfy parameter requirements.
  • Stub: Use when the dependency must provide predefined responses for the logic being tested.

In the original example, the object was acting as a stub because it provided a predefined response to queryUserById. The adjusted example above is a clearer demonstration of a dummy object.

Here’s another example of using a dummy object in unit testing, still within the context of UserService and DatabaseConnection but focusing on a different scenario.


Scenario:

Suppose we want to test a method in the UserService class that generates an audit log message for a user. The method takes a userId and formats an audit message, but it does not interact with the database at all.

In this case, the DatabaseConnection is irrelevant to the test, so we can use a dummy object to fulfill the constructor dependency.


Classes:

DatabaseConnection:

public class DatabaseConnection {
    public void connect() {
        System.out.println("Connecting to the database...");
    }

    public void disconnect() {
        System.out.println("Disconnecting from the database...");
    }

    public String queryUserById(int userId) {
        return "Real User"; // Placeholder
    }
}

UserService:

public class UserService {

    private DatabaseConnection databaseConnection;

    public UserService(DatabaseConnection databaseConnection) {
        this.databaseConnection = databaseConnection;
    }

    public String generateAuditLog(int userId) {
        // This method does not interact with the database
        return "Audit Log: User " + userId + " accessed the system.";
    }
}

Dummy Object Implementation:

The dummy object is created simply to fulfill the dependency in the UserService constructor but will not be used during the test.

// Dummy implementation of DatabaseConnection
public class DummyDatabaseConnection extends DatabaseConnection {
    // No implementation is needed as it will not be used
}

Unit Test:

Here’s how the test would look:

import org.junit.Test;
import static org.junit.Assert.*;

public class UserServiceTest {

    @Test
    public void testGenerateAuditLog() {
        // Create a dummy database connection
        DatabaseConnection dummyConnection = new DummyDatabaseConnection();

        // Create the UserService instance with the dummy connection
        UserService userService = new UserService(dummyConnection);

        // Perform the test
        String auditLog = userService.generateAuditLog(123);

        // Validate the result
        assertEquals("Audit Log: User 123 accessed the system.", auditLog);
    }
}

Key Points:

  1. Dummy Object:
    • The DummyDatabaseConnection is passed to the UserService constructor but is never used within the generateAuditLog method.
  2. Focus of the Test:
    • The test focuses entirely on the generateAuditLog method’s functionality, ignoring the database connection.
  3. Minimal Effort:
    • The dummy object has no implementation or behavior because it is irrelevant to the tested method.

Comparison to the Previous Example:

In this example, we are explicitly testing a method (generateAuditLog) that does not depend on any behavior from DatabaseConnection. This makes the DatabaseConnection a perfect candidate for being replaced with a dummy object, as it serves no purpose beyond satisfying the constructor requirement.

Here’s a different example of a dummy object in unit testing, with a different context to demonstrate its usage more broadly.


Context: Email Service

Imagine you are building a system that sends emails. The system has a class called EmailService that uses an EmailSender dependency to send emails. However, some methods in EmailService do not actually send emails but still require an EmailSender dependency to construct the object.


Classes:

EmailSender:

This interface defines the behavior for sending emails.

public interface EmailSender {
    void sendEmail(String to, String subject, String body);
}

EmailService:

This class uses an EmailSender to send emails, but some methods (e.g., validating email addresses) do not require the EmailSender.

public class EmailService {

    private EmailSender emailSender;

    public EmailService(EmailSender emailSender) {
        this.emailSender = emailSender;
    }

    // Method to send an email (not tested in this example)
    public void sendWelcomeEmail(String to) {
        String subject = "Welcome!";
        String body = "Thank you for signing up.";
        emailSender.sendEmail(to, subject, body);
    }

    // Method to validate an email address
    public boolean isValidEmail(String email) {
        return email != null && email.contains("@") && email.endsWith(".com");
    }
}

Dummy Implementation:

The dummy implementation of EmailSender is created only to satisfy the constructor dependency for EmailService.

// Dummy implementation of EmailSender
public class DummyEmailSender implements EmailSender {
    @Override
    public void sendEmail(String to, String subject, String body) {
        // No implementation needed, as this is a dummy
    }
}

Unit Test:

We will test the isValidEmail method, which does not require the EmailSender to perform its logic.

import org.junit.Test;
import static org.junit.Assert.*;

public class EmailServiceTest {

    @Test
    public void testIsValidEmail() {
        // Create a dummy email sender
        EmailSender dummyEmailSender = new DummyEmailSender();

        // Create the EmailService instance with the dummy sender
        EmailService emailService = new EmailService(dummyEmailSender);

        // Perform tests on the email validation method
        assertTrue(emailService.isValidEmail("test@example.com"));
        assertFalse(emailService.isValidEmail("test@example")); // Missing .com
        assertFalse(emailService.isValidEmail(null));           // Null email
        assertFalse(emailService.isValidEmail(""));             // Empty email
    }
}

Key Points of This Example:

  1. Dummy Object:
    • The DummyEmailSender is created solely to satisfy the dependency required by the EmailService constructor.
    • It is not used in the method being tested (isValidEmail).
  2. Test Focus:
    • The test focuses entirely on the logic of the isValidEmail method, which has nothing to do with the EmailSender.
  3. Simplified Testing:
    • By using a dummy, we avoid the complexity of implementing or mocking email-sending functionality, which is irrelevant for this test.

Why It’s a Dummy and Not a Stub:

  • The DummyEmailSender does nothing and is not involved in the test logic.
  • If we had made it return some predefined value or track whether sendEmail was called, it would become a stub or a mock, depending on its behavior.

This illustrates a pure dummy object in a different real-world context!

Here’s another example of using a dummy object, this time in the context of logging.


Context: Payment Processor

Imagine you’re building a system where a PaymentProcessor class handles payments. It uses a Logger to log messages about the payment process. However, some methods (e.g., validating payment amounts) don’t require logging but still depend on the Logger as part of the PaymentProcessor constructor.


Classes:

Logger:

A simple interface for logging.

public interface Logger {
    void log(String message);
}

PaymentProcessor:

This class processes payments and logs relevant messages. However, some methods (like validation) don’t need to log anything.

public class PaymentProcessor {

    private Logger logger;

    public PaymentProcessor(Logger logger) {
        this.logger = logger;
    }

    // Method to process a payment (not tested in this example)
    public void processPayment(String paymentId, double amount) {
        logger.log("Processing payment ID: " + paymentId + " with amount: " + amount);
        // Payment processing logic
    }

    // Method to validate a payment amount
    public boolean isValidAmount(double amount) {
        return amount > 0 && amount <= 10000; // Only positive amounts under $10,000 are valid
    }
}

Dummy Implementation:

The dummy Logger is created only to satisfy the dependency requirement of the PaymentProcessor.

// Dummy implementation of Logger
public class DummyLogger implements Logger {
    @Override
    public void log(String message) {
        // No implementation needed
    }
}

Unit Test:

We will test the isValidAmount method, which does not depend on the Logger.

import org.junit.Test;
import static org.junit.Assert.*;

public class PaymentProcessorTest {

    @Test
    public void testIsValidAmount() {
        // Create a dummy logger
        Logger dummyLogger = new DummyLogger();

        // Create the PaymentProcessor instance with the dummy logger
        PaymentProcessor paymentProcessor = new PaymentProcessor(dummyLogger);

        // Perform tests on the amount validation method
        assertTrue(paymentProcessor.isValidAmount(100.50)); // Valid amount
        assertTrue(paymentProcessor.isValidAmount(9999.99)); // Edge case: just under $10,000
        assertFalse(paymentProcessor.isValidAmount(-10.00)); // Negative amount
        assertFalse(paymentProcessor.isValidAmount(0)); // Zero is invalid
        assertFalse(paymentProcessor.isValidAmount(15000)); // Exceeds $10,000
    }
}

Key Points of This Example:

  1. Dummy Object:
    • The DummyLogger is used solely to satisfy the dependency required by the PaymentProcessor constructor.
    • It is not used in the method under test (isValidAmount).
  2. Focus of the Test:
    • The test is focused entirely on the isValidAmount method’s logic, ignoring any concerns related to logging.
  3. Simplified Test Setup:
    • Using the dummy object avoids adding unnecessary complexity to the test, such as mocking or stubbing the Logger.

Why It’s a Dummy and Not a Stub:

  • The DummyLogger provides no behavior or return values.
  • It exists only to fulfill the constructor requirement without contributing to the test logic.

This is a clean demonstration of a dummy object in a logging context, which is a common real-world use case!

Here’s another example using a dummy object, this time in the context of a file uploading service.


Context: File Uploader

Imagine you’re building a system with a FileUploader class responsible for uploading files to a remote server. This class depends on a StorageService to handle the actual file storage. However, some methods in FileUploader, such as file size validation, do not interact with the StorageService.


Classes:

StorageService:

An interface representing a storage service for file uploads.

public interface StorageService {
    void uploadFile(String fileName, byte[] data);
}

FileUploader:

This class handles file uploads. It uses a StorageService to upload files, but some methods, like validating the file size, don’t require StorageService.

public class FileUploader {

    private StorageService storageService;

    public FileUploader(StorageService storageService) {
        this.storageService = storageService;
    }

    // Method to upload a file (not tested in this example)
    public void upload(String fileName, byte[] data) {
        if (data == null || data.length == 0) {
            throw new IllegalArgumentException("File data cannot be empty");
        }
        storageService.uploadFile(fileName, data);
    }

    // Method to validate file size
    public boolean isValidFileSize(int fileSizeInBytes) {
        return fileSizeInBytes > 0 && fileSizeInBytes <= 10_000_000; // Limit: 10 MB
    }
}

Dummy Implementation:

The dummy StorageService is created only to satisfy the dependency in the FileUploader constructor.

// Dummy implementation of StorageService
public class DummyStorageService implements StorageService {
    @Override
    public void uploadFile(String fileName, byte[] data) {
        // No implementation needed, as this is a dummy
    }
}

Unit Test:

We will test the isValidFileSize method, which does not depend on the StorageService.

import org.junit.Test;
import static org.junit.Assert.*;

public class FileUploaderTest {

    @Test
    public void testIsValidFileSize() {
        // Create a dummy storage service
        StorageService dummyStorageService = new DummyStorageService();

        // Create the FileUploader instance with the dummy storage service
        FileUploader fileUploader = new FileUploader(dummyStorageService);

        // Perform tests on the file size validation method
        assertTrue(fileUploader.isValidFileSize(100));           // Valid small file size
        assertTrue(fileUploader.isValidFileSize(10_000_000));   // Edge case: exactly 10 MB
        assertFalse(fileUploader.isValidFileSize(0));           // Invalid: zero file size
        assertFalse(fileUploader.isValidFileSize(-1));          // Invalid: negative file size
        assertFalse(fileUploader.isValidFileSize(20_000_000));  // Invalid: exceeds 10 MB
    }
}

Key Points of This Example:

  1. Dummy Object:
    • The DummyStorageService is used solely to fulfill the dependency required by the FileUploader constructor.
    • It has no implementation or behavior because the test doesn’t require the StorageService.
  2. Test Focus:
    • The test focuses entirely on the logic of isValidFileSize, which does not rely on the StorageService.
  3. Simplified Setup:
    • By using a dummy object, the test avoids unnecessary complexity, such as creating a mock storage service or providing a real implementation.

Why It’s a Dummy and Not a Stub:

  • The DummyStorageService does not provide any functionality or return values.
  • Its only purpose is to satisfy the constructor requirement of FileUploader.

This example illustrates another practical use of a dummy object in a file uploading scenario!

Here’s another example of using a dummy object, this time in the context of a notification system.


Context: Notification System

Imagine a NotificationService class that sends notifications. It depends on a MessageFormatter to format messages. However, some methods in the NotificationService, such as validating recipient information, do not require the MessageFormatter but still depend on it for construction.


Classes:

MessageFormatter:

An interface for formatting messages.

public interface MessageFormatter {
    String formatMessage(String template, Object... args);
}

NotificationService:

This class sends notifications and uses MessageFormatter to format the notification messages. However, some methods like validating recipients do not interact with the MessageFormatter.

public class NotificationService {

    private MessageFormatter messageFormatter;

    public NotificationService(MessageFormatter messageFormatter) {
        this.messageFormatter = messageFormatter;
    }

    // Method to send a notification (not tested in this example)
    public void sendNotification(String recipient, String template, Object... args) {
        if (recipient == null || recipient.isEmpty()) {
            throw new IllegalArgumentException("Recipient cannot be null or empty");
        }
        String message = messageFormatter.formatMessage(template, args);
        System.out.println("Sending to " + recipient + ": " + message);
    }

    // Method to validate a recipient's email address
    public boolean isValidRecipient(String email) {
        return email != null && email.contains("@") && email.endsWith(".com");
    }
}

Dummy Implementation:

The dummy MessageFormatter is created just to satisfy the dependency in the NotificationService constructor.

// Dummy implementation of MessageFormatter
public class DummyMessageFormatter implements MessageFormatter {
    @Override
    public String formatMessage(String template, Object... args) {
        // No implementation needed for this dummy
        return "";
    }
}

Unit Test:

We will test the isValidRecipient method, which does not depend on the MessageFormatter.

import org.junit.Test;
import static org.junit.Assert.*;

public class NotificationServiceTest {

    @Test
    public void testIsValidRecipient() {
        // Create a dummy message formatter
        MessageFormatter dummyFormatter = new DummyMessageFormatter();

        // Create the NotificationService instance with the dummy formatter
        NotificationService notificationService = new NotificationService(dummyFormatter);

        // Perform tests on the recipient validation method
        assertTrue(notificationService.isValidRecipient("user@example.com"));     // Valid email
        assertFalse(notificationService.isValidRecipient("user@example"));       // Missing .com
        assertFalse(notificationService.isValidRecipient(null));                 // Null email
        assertFalse(notificationService.isValidRecipient(""));                   // Empty email
        assertFalse(notificationService.isValidRecipient("user@domain"));        // No TLD
    }
}

Key Points of This Example:

  1. Dummy Object:
    • The DummyMessageFormatter is created solely to fulfill the dependency required by the NotificationService constructor.
    • It is not used in the test logic.
  2. Test Focus:
    • The test focuses entirely on the isValidRecipient method’s logic, which does not involve message formatting.
  3. Simplified Testing:
    • Using a dummy object avoids the need to implement or mock MessageFormatter, keeping the test simple and focused.

Why It’s a Dummy and Not a Stub:

  • The DummyMessageFormatter provides no meaningful behavior and is not called during the test.
  • It only exists to meet the constructor requirement of NotificationService.

This example demonstrates how dummy objects can be used in a notification system, another real-world scenario!

Here’s another example using a dummy object, this time in the context of a library management system.


Context: Library Management System

In a library system, there is a BookService class responsible for managing books. It depends on an EmailNotifier to send email notifications when books are borrowed or returned. However, some methods in BookService, such as calculating late fees, do not require the EmailNotifier.


Classes:

EmailNotifier:

An interface for sending email notifications.

public interface EmailNotifier {
    void sendNotification(String email, String message);
}

BookService:

This class manages books and sends notifications when books are borrowed or returned. It uses EmailNotifier, but certain methods (e.g., calculateLateFee) do not depend on it.

public class BookService {

    private EmailNotifier emailNotifier;

    public BookService(EmailNotifier emailNotifier) {
        this.emailNotifier = emailNotifier;
    }

    // Method to borrow a book (not tested in this example)
    public void borrowBook(String bookTitle, String userEmail) {
        emailNotifier.sendNotification(userEmail, "You have borrowed: " + bookTitle);
    }

    // Method to calculate the late fee
    public double calculateLateFee(int daysLate) {
        double dailyFee = 0.50; // $0.50 per day late
        return daysLate > 0 ? daysLate * dailyFee : 0.0;
    }
}

Dummy Implementation:

The dummy EmailNotifier is created solely to satisfy the dependency required by the BookService constructor.

// Dummy implementation of EmailNotifier
public class DummyEmailNotifier implements EmailNotifier {
    @Override
    public void sendNotification(String email, String message) {
        // No implementation needed for this dummy
    }
}

Unit Test:

We will test the calculateLateFee method, which does not depend on the EmailNotifier.

import org.junit.Test;
import static org.junit.Assert.*;

public class BookServiceTest {

    @Test
    public void testCalculateLateFee() {
        // Create a dummy email notifier
        EmailNotifier dummyNotifier = new DummyEmailNotifier();

        // Create the BookService instance with the dummy notifier
        BookService bookService = new BookService(dummyNotifier);

        // Perform tests on the late fee calculation method
        assertEquals(0.0, bookService.calculateLateFee(0), 0.01);          // No late days
        assertEquals(1.50, bookService.calculateLateFee(3), 0.01);        // 3 days late
        assertEquals(0.0, bookService.calculateLateFee(-5), 0.01);        // Negative days late
        assertEquals(25.0, bookService.calculateLateFee(50), 0.01);       // 50 days late
    }
}

Key Points of This Example:

  1. Dummy Object:
    • The DummyEmailNotifier is used solely to fulfill the dependency in the BookService constructor.
    • It has no implementation or behavior because the test doesn’t involve notifications.
  2. Focus of the Test:
    • The test focuses entirely on the calculateLateFee method, which does not depend on EmailNotifier.
  3. Simplified Test Setup:
    • Using a dummy object avoids unnecessary complexity, such as creating a mock notifier, making the test concise and focused.

Why It’s a Dummy and Not a Stub:

  • The DummyEmailNotifier is not invoked during the test and provides no functionality or behavior.
  • It exists purely to satisfy the constructor dependency of BookService.

This example illustrates a dummy object in a library management context, another common real-world scenario!

Share11Tweet7Share2
Next Post

Unit Testing with Dummy Object in Test Doubles

Next Post

Unit Testing with Dummy Object in Test Doubles

Stub Object in Unit testing with Java

Unit testing - Stub Object in Java

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

Soft skills Outlines

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Categories

  • Frameworks (22)
  • Programming Languages (4)
  • Tech (1)

© 2025 Bawabaa.com

No Result
View All Result
  • Home
  • Products
  • Services
    • Courses
    • Pathways
  • Solutions
  • Plans
  • Resources
    • List of Curated Edtech Websites
    • Pathways
    • Blog
      • Programming Languages
      • Tech
      • Graphic Design
      • Inspiration
      • Software Testing
      • Operating Systems
      • Frameworks

© 2025 Bawabaa.com