Current post type: post
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.
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.
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.
Here, we’ll focus on a scenario where the dependency is present but irrelevant for the specific test.
// 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;
}
}
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
}
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);
}
}
DatabaseConnection
is passed to the UserService
constructor but is not used in the method being tested (formatUserName
).formatUserName
, not the interaction with DatabaseConnection
.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.
UserService
and DatabaseConnection
but focusing on a different 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.
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.";
}
}
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
}
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);
}
}
DummyDatabaseConnection
is passed to the UserService
constructor but is never used within the generateAuditLog
method.generateAuditLog
method’s functionality, ignoring the database connection.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.
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.
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");
}
}
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
}
}
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
}
}
DummyEmailSender
is created solely to satisfy the dependency required by the EmailService
constructor.isValidEmail
).isValidEmail
method, which has nothing to do with the EmailSender
.DummyEmailSender
does nothing and is not involved in the test logic.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!
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.
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
}
}
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
}
}
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
}
}
DummyLogger
is used solely to satisfy the dependency required by the PaymentProcessor
constructor.isValidAmount
).isValidAmount
method’s logic, ignoring any concerns related to logging.Logger
.DummyLogger
provides no behavior or return values.This is a clean demonstration of a dummy object in a logging context, which is a common real-world use case!
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
.
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
}
}
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
}
}
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
}
}
DummyStorageService
is used solely to fulfill the dependency required by the FileUploader
constructor.StorageService
.isValidFileSize
, which does not rely on the StorageService
.DummyStorageService
does not provide any functionality or return values.FileUploader
.This example illustrates another practical use of a dummy object in a file uploading scenario!
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.
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");
}
}
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 "";
}
}
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
}
}
DummyMessageFormatter
is created solely to fulfill the dependency required by the NotificationService
constructor.isValidRecipient
method’s logic, which does not involve message formatting.MessageFormatter
, keeping the test simple and focused.DummyMessageFormatter
provides no meaningful behavior and is not called during the test.NotificationService
.This example demonstrates how dummy objects can be used in a notification system, another real-world scenario!
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
.
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;
}
}
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
}
}
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
}
}
DummyEmailNotifier
is used solely to fulfill the dependency in the BookService
constructor.calculateLateFee
method, which does not depend on EmailNotifier
.DummyEmailNotifier
is not invoked during the test and provides no functionality or behavior.BookService
.This example illustrates a dummy object in a library management context, another common real-world scenario!
By MOUSTAFA ALSAYEH