Introduction to Dummies

After we have finished this lesson, we

  • Know what a dummy is.
  • Understand when we should use dummies.
  • Know how we should create dummies.
This lesson assumes that you are familiar with the system under test .

What Is a Dummy?

A dummy is simply an object that is irrelevant for our test case, but we still have to provide it. The two most common reasons why we have to provide a dummy are:

  • Our code won’t compile.
  • Our test throws an exception because a mandatory value is missing.

Let’s find out when we will run into these problems.

When Should We Use Dummies?

As I already suggested, we should use dummies when an object or a value is irrelevant for our test case, and we need this object or value only because our code doesn’t compile or our test case doesn’t pass because it throws an exception.

Let’s take a look at some examples that demonstrate these situations.

Passing a Dummy as a Method Parameter

It is quite obvious that if the tested method takes two method parameters, we cannot invoke it and provide only one method parameter because our code won’t compile. If only one method parameter is relevant for our test case, we can replace the other method parameter with a dummy.

Again, this is self-evident, but this naive example is not the only situation when we have to pass a dummy as a method parameter.

Let’s assume that we are writing unit tests for a method that creates a copy of an existing task. This method takes two method parameters:

  1. The id of the source task.
  2. A LoggedInUser object that contains the information of the user who is marked as the creator of the new task.

Also, this method can throw two exceptions which are described in the following:

  • It throws a NullPointerException if any of its method parameters is null.
  • It throws a NotFoundException if the source task is not found.

The source code of the system under test looks as follows:

 1import com.google.common.base.Preconditions;
 2import java.util.Optional;
 3
 4public class TaskService {
 5
 6	private final TaskRepository repository;
 7
 8	public TaskService(TaskRepository repository) {
 9		this.repository = repository;
10	}
11
12	public Task createCopyOf(Long sourceTaskId, LoggedInUser creator) {
13		Preconditions.checkNotNull(sourceTaskId);
14		Preconditions.checkNotNull(creator);
15
16		Optional<Task> found = taskRepository.findById(sourceTaskId);
17		Task source = found.orElseThrow(NotFoundException::new);
18
19		//The rest of this method is omitted
20	}
21}

Now, if we want to write a unit test which ensures that the NotFoundException is thrown when the source task is not found, we don’t really care about the second method parameter because it’s irrelevant for our test case. However, we still have to pass something as the second method parameter or our test won’t pass. That is why we we will use a dummy.

Using a Dummy as a Mandatory Value of the Created Object

When we create the test data that is required by our test case, sometimes we have to create new objects that have mandatory properties. If these properties are not specified or an invalid property value is used, our code throws an exception.

For example, the Task class has a builder which ensures that we cannot create a new Task object that doesn’t have a creator and a title. The source code of the Builder inner class looks as follows:

 1public static class Builder {
 2
 3    private Long id;
 4    private Assignee assignee;
 5    private Creator creator;
 6    private String description;
 7    private String title;
 8
 9    private Builder() {}
10
11    //Other methods are omitted for the sake of clarity
12
13    public Task build () {
14        Task task = new Task(this);
15
16        checkCreator(task);
17        checkTitle(task);
18
19        return task;
20    }
21
22    private void checkCreator(Task task) {
23        Creator creator = task.getCreator();
24        if (creator == null) {
25            throw new NullPointerException();
26        }
27    }
28
29    private void checkTitle(Task task) {
30        String title = task.getTitle();
31        if (title == null) {
32            throw new NullPointerException();
33        }
34
35        if (title.isEmpty()) {
36            throw new IllegalStateException();
37        }
38    }
39}

If we want to create a new Task object, we have to either specify the values of the creator and title fields or create a mechanism that uses dummies if we don’t specify the values of these fields. Actually, we have already done that .

At this point we should have a pretty good idea when we should use dummies. Let’s out how we should create them.

How Should We Create Dummies?

I will admit that creating dummies is not exactly rocket science. However, the different options have slight differences, and that is why we will take a look at four different techniques that we can use for creating dummies.

First, we can use an insane value. We have already used this technique for emphasizing the state of the created object , but I will explain it briefly here as well.

The idea of this technique is to simply use a value which makes it clear that the value is not used by the system under test. Also, we should name this value by creating either a constant or a local variable that contains our dummy value because this way we don’t add magic numbers into our tests.

This technique is naturally useful only if we need to replace a primitive data type with a dummy.

Second, we can use the new keyword. Because a dummy is just a “placeholder” that is needed only because otherwise our test fails, we don’t have to set the field values of the created object. That is why using the new keyword is a simple solution as long as the class in question provides a no-argument constructor and the visibility of the constructor doesn’t prevent us from using it.

Also, we should give a proper name for our dummy before we pass it forward to the system under test. This makes our test a bit easier to read.

The following example demonstrates how we can create a dummy object by using the new keyword:

 1public class TaskServiceTest {
 2
 3	private static final Long SOURCE_TASK_ID = 3L;
 4
 5	private TaskService service;
 6
 7	//Setup is omitted because of clarity
 8
 9	@Test
10	public void createCopyOf() {
11		LoggedInUser notImportant = new LoggedInUser();
12		service.createCopyOf(SOURCE_TASK_ID, notImportant);
13	}
14}

Third, we can use a mocking framework. This is a good choice if we cannot use the new keyword or we would have to create our dummy by creating a new “dummy class” that implements an interface.

Again, we should give a proper name for our dummy before we pass it forward to the system under test.

The following example demonstrates how we can create a dummy object by using Mockito:

 1import static org.mockito.Mockito.mock;
 2
 3public class TaskServiceTest {
 4
 5	private static final Long SOURCE_TASK_ID = 3L;
 6
 7	private TaskService service;
 8
 9	//Setup is omitted because of clarity
10
11	@Test
12	public void createCopyOf() {
13		LoggedInUser notImportant = mock(LoggedInUser.class);
14		service.createCopyOf(SOURCE_TASK_ID, notImportant);
15	}
16}
We will talk more about this in the following lessons when we learn to create test doubles with Mockito. That being said, we should know that we cannot create a dummy object from a class with Mockito if the class is final.

Fourth, we can use a factory method. If we cannot use the other techniques, we should create our dummy object by using a factory method.

If we decide to use this technique, we shouldn’t use our existing factory methods because typically the names of these methods are misleading and they initialize the created object by using sane data. This is a bit misleading because it gives the impression that the created object is not irrelevant.

We should create a new factory method, give it a name which states that the created object is a dummy, and create our dummy objects by using that method. Also, our new factory method should set only the mandatory property values by using insane default values (aka dummies).

The following example demonstrates how we can create a dummy object by using a factory method:

 1public class TaskServiceTest {
 2
 3	private static final Long SOURCE_TASK_ID = 3L;
 4
 5	private TaskService service;
 6
 7	//Setup is omitted because of clarity
 8
 9	@Test
10	public void createCopyOf() {
11		LoggedInUser notImportant = LoggedInUserFactory.dummyUser();
12		service.createCopyOf(SOURCE_TASK_ID, notImportant);
13	}
14}
If we already have a test data builder, we should add this factory method into our test data builder class and leverage our test data builder when we create the required dummy object. This technique is described in the lesson titled: Emphasizing the State of the Created Object .

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us four things:

  • A dummy is an object that this is irrelevant for the test case.
  • We have to provide a dummy to the system under test because otherwise our code won’t compile or our test won’t pass.
  • If our dummy is a primitive data type, we should use an insane value.
  • If our dummy is an object, we can create it by using either the new keyword, a mocking tool, or a factory method.