Introduction to Factory Methods

After we have finished this lesson, we

  • Know what a factory method is.
  • Can write good factory methods (in theory).
  • Understand where we can put our factory methods.

This lesson belongs to the 2nd version of the Test With Spring course. If you want to take a look at the “old” lesson, you should click this link .

This lesson assumes that:

What Is a Factory Method?

Wikipedia defines the factory method pattern as follows:

In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created.

When we talk about factory methods, we are not talking about methods that fulfill this definition. When we write tests, a factory method is a method that:

  • Creates a new object, sets the field values of the created object, and returns the created object.
  • Can take method parameters, but it doesn’t have to.

Here are some examples of factory methods:

1public Task createTask() {
2    //Create a new Task object and return it
3}
4
5public Task createTask(String title, String description) {
6    //create a new Task object and return it
7}

We have now described what a factory method is. Next, we will find out how we can write good factory methods.

Writing Good Factory Methods

Before we can write a factory method, we have to:

First, we have to decide what its purpose is. Using a factory method is a good choice if we want to emphasize a specific property or properties of the created object. On the other hand, we shouldn’t use factory methods as general purpose builders because we have to pass the required information as method parameters.

For example, if we want to create an open task, it’s a good idea to use a factory method if we don’t care about the other properties of the created object. On the other hand, if we want to create a Task object and pass all field values as method parameters, we shouldn’t use a factory method because using it has no benefits over using the new keyword.

Second, we have to figure out a good name for it. This is crucial because the whole idea of using factory methods is based on the assumption that they make our code easier to read, write, and maintain. That’s why we must reject generic names and find a name that identifies the essence of the created object.

For example, createTask() isn’t a very good name because it doesn’t help us to understand what kind of task is created by our factory method. On the other hand, createOpenTask() is a good name because it identifies the essence of the created object.

Third, we have to decide how many method parameters it has. If we want to emphasize a specific property or properties of the created object, we can use these two options:

  • We can use the name of our factory method.
  • If our test methods require objects which have slightly different field values, we can emphasize these field values by using method parameters.

If a factory method has a clear purpose, typically it doesn’t require many method parameters. That’s why we should aim to minimize the number of method parameters.

I won’t specify an exact number because this is a decision that you have to make yourself. However, when you start making mistakes and/or creating new objects feels a bit cumbersome, your factory method most likely has too many method parameters.

Let’s take a look at some examples that demonstrate how we should use method parameters when we write factory methods.

 1//This a is bad choice because using this factory method
 2//isn't a better option than using the new keyword.
 3public MailingAddress createAddress(String receiver,
 4		String streetAddress,
 5		String postOfficeBox,
 6		String postCode,
 7		String city,
 8		String state,
 9		String country
10) {
11
12}
13
14//This is a good choice if we care only about the fact that the created task
15//is an open task
16public Task createOpenTask() {
17
18}
19
20//This is a good choice if we care only about the fact that the created task
21//was closed by the specified person
22public Task createClosedTask(Long closerId) {
23
24}
25
26//This is a good choice if we care only about the fact that the created task
27//was closed by the specified person who used the given resolution
28public Task createClosedTask(Long closerId, TaskResolution resolution) {
29
30}

We are now familiar with the basics of writing good factory methods. Let’s move on and find out where we should put our factory methods.

Where Should We Put Our Factory Methods?

When we write a new factory method, we can use one of these two options:

First, we can add our factory method to our test class. This helps us to get rid off duplicate code as long as all test methods which use our factory method are found from the same test class.

For example, if we use this approach, our test class could look as follows:

 1import org.junit.Test;
 2
 3public class TaskTest {
 4
 5	@Test
 6	public void testOne() {
 7		Task task = createTask();
 8	}
 9
10	@Test
11	public void testTwo() {
12		Task task = createTask();
13	}
14
15	private Task createTask() {
16		//Create a new task and return the created object
17	}
18}

Second, we can create a so called object mother class and add our factory method to that class. This helps us to get rid off duplicate code when our factory method is used by multiple test classes.

For example, if we use this approach our object mother class could look as follows:

1public final class TaskFactory {
2
3	private TaskFactory() {}
4
5	public static Task createTask() {
6		//Create a new task and return the created object
7	}
8}

The test classes which use this factory method could look as follows:

1import org.junit.Test;
2
3public class TaskTestOne {
4
5	@Test
6	public void testOne() {
7		Task task = TaskFactory.createTask();
8	}
9}
1import org.junit.Test;
2
3public class TaskTestTwo {
4
5	@Test
6	public void testTwo() {
7		Task task = TaskFactory.createTask();
8	}
9}
We will talk more about these techniques in the next lessons of this topic. That’s why I won’t describe the details in this lesson.

We are now familiar with the basics. Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us five things:

  • A factory method creates the test data that’s used by our test methods.
  • A good factory method has a clear purpose.
  • The name of a factory method must identify the essence of the created object.
  • A good factory method has as few method parameters as possible.
  • We can put our factory methods to a test class or to an object mother class.