Creating Test Data With Groovy

After we have finished this lesson, we:

  • Can create new objects by using named argument constructors.
  • Know how we can set the field values of an object.

Creating New Objects

If the instantiated class has no constructor or it has a constructor that has no constructor arguments, we can use a so called named argument constructor. We can specify the field values of the created object by using key/value pairs, and the constructor creates a new object and sets the field values of the created object.

Let’s take a look at a simple example that demonstrates how named argument constructors work.

The TaskFormDTO class is a simple data transfer object that contains the information of the created or updated task. It has three fields which contain the id, description, and title of the task. Also, it has a no-argument constructor.

The source code of the TaskFormDTO class looks as follows:

 1public class TaskFormDTO {
 2
 3    private Long id;
 4
 5    private String description;
 6
 7    private String title;
 8
 9    public TaskFormDTO() {
10
11    }
12
13    public Long getId() {
14        return id;
15    }
16
17    public String getDescription() {
18        return description;
19    }
20
21    public String getTitle() {
22        return title;
23    }
24
25    public void setId(Long id) {
26        this.id = id;
27    }
28
29    public void setDescription(String description) {
30        this.description = description;
31    }
32
33    public void setTitle(String title) {
34        this.title = title;
35    }
36}

Let’s create a new TaskFormDTO object and specify the field values of the id, title, and description fields by using a named argument constructor. After we have created a new TaskFormDTO object, the source code of our specification class looks as follows:

 1import org.junit.experimental.categories.Category
 2import spock.lang.Specification
 3
 4@Category(UnitTest.class)
 5class ConstructorSpec extends Specification {
 6
 7    def 'Create a new object by using the named argument constructor'() {
 8
 9        when: 'A new task form object is created'
10        def object = new TaskFormDTO(id: 1L,
11				title: 'title',
12				description: 'description'
13		)
14
15        then: 'Should create a new object with correct information'
16        object.id == 1L
17        object.title == 'title'
18        object.description == 'description'
19    }
20}

As we can see, named argument constructors can help us to save a few lines of code if we want to pass only a few field values. Also, because the constructor arguments have names, our test code is easy to read.

That being said, we shouldn’t use this technique if we are creating stateful objects because named constructor arguments don’t help us to describe the meaning of different property values and this techniques forces us to scatter the object creation logic to our specification classes. In other words, if we use named argument constructors for this purpose, we write tests that are hard to read, write, and maintain.

Let’s move on and find out how we can set the field values of an object.

Setting the Field Values of an Object

If we want to set the field values of an object, we can use either properties, direct field access, or reflection. Next, we will find out how we can set the field values of an object by using properties.

Using Properties

A property is basically a combination of a private field and a getter or a setter. We can access a property by using its name and the appropriate getter or setter is called transparently.

Let’s create a new TaskFormDTO object and set the values of the id, title, and description fields by using properties. After we have created a new object, the source code of our specification class looks as follows:

 1import org.junit.experimental.categories.Category
 2import spock.lang.Specification
 3
 4@Category(UnitTest.class)
 5class PropertySpec extends Specification {
 6
 7    def 'Create a new object and set its property values'() {
 8
 9        when: 'We create a new object and set its property values'
10        def object = new TaskFormDTO()
11        object.id = 1L
12        object.title = 'title'
13        object.description = 'description'
14
15        then: 'Should create a new object and set its property values'
16        object.id == 1L
17        object.title == 'title'
18        object.description == 'description'
19    }
20}

If we are accessing a property “inside” the class that defines it by using the this keyword, our code will access the field and the getter or setter is not invoked.

Additional Reading:

Let’s move on and find out how we can set the field values of an object by using the direct field access operator.

Using the Direct Field Access Operator

If we want to access a field of an object and we cannot invoke the getter or setter (or we just don’t want to do it), we can access the field by using the direct field access operator (.@).

Let’s use this operator when we implement a test data builder that creates new Task objects. Because this builder is an exact copy of the builder that we created during the starter package , I won’t describe its implementation here.

Our goal is to finish the implementation of the build() method. We can do this by setting the field values of the private fields that cannot be set by using the builder provided by the Task class. After we have set the field values of these fields, the source code of our builder class looks as follows:

 1import java.time.ZonedDateTime
 2
 3class TaskBuilder {
 4
 5    private static final NOT_IN_USE = -1L
 6    private static final NOT_IMPORTANT = 'NOT_IMPORTANT'
 7    private static final NOT_SET = ZonedDateTime
 8            .now()
 9            .minusYears(9999)
10
11    private assigneeId = null
12    private closerId = null
13    private creationTime = NOT_SET
14    private creatorId = NOT_IN_USE
15    private description = NOT_IMPORTANT
16    private id = NOT_IN_USE
17    private modificationTime = NOT_SET
18    private modifierId = NOT_IN_USE
19    private resolution = null
20    private status = null
21    private tags = new HashSet<>()
22    private title = NOT_IMPORTANT
23
24	//The methods that set field values are omitted
25
26    def build() {
27        def task = Task.builder
28                .withCreator(creatorId)
29                .withDescription(description)
30                .withTitle(title)
31                .build()
32
33        if (assigneeId != null) {
34            task.@assignee = new Assignee(assigneeId)
35        }
36
37        if (closerId != null) {
38            task.@closer = new Closer(closerId)
39        }
40
41        if (modifierId != null) {
42            task.@modifier = new Modifier(modifierId)
43        }
44
45        task.@status = status
46        task.@resolution = resolution
47        task.@tags = tags
48
49        return task
50    }
51}

If we are trying to access the fields of a Java class, we don’t have to use the direct field access operator because we can use either properties or reflection. However, it can be useful if we are writing tests for Groovy code. If you want to get more details about this, you should take a look at the following links:

Additional Reading:

Our test data builder is not finished yet because we haven’t set the field values of the fields that are declared by the AbstractEntity class. Next, we will find out how we can set the field values of these fields.

Using Reflection

The Task class extends the AbstractEntity class which provides common fields that are shared by all entities of our application. The source code of the AbstractEntity class looks as follows:

 1import javax.persistence.*;
 2import java.time.ZonedDateTime;
 3
 4@MappedSuperclass
 5public abstract class AbstractEntity {
 6
 7    @Id
 8    @GeneratedValue(strategy = GenerationType.IDENTITY)
 9    private Long id;
10
11    @Column(name = "creation_time", nullable = false)
12    private ZonedDateTime creationTime;
13
14    @Column(name = "modification_time")
15    private ZonedDateTime modificationTime;
16
17    @Version
18    private Long version;
19
20	//Getters are omitted
21}

As we can see, the AbstractEntity class declares four private fields that have getter methods but no setter methods. This means that we cannot set the field values of these fields by using properties. Also, because private fields are not inherited, we cannot set the values of these fields by using the direct field access operator. That’s why we have to use reflection.

If we want to set field values by using reflection, we have to follow these steps:

First, we have to implement a utility class that can set the field values of an object by using reflection. We can implement this class by following these steps:

  1. Create a final class called ReflectionFieldSetter.
  2. Add a private constructor to the created class.
  3. Add a public and static setFieldValue() method to created class. This method takes the target object, the name of the field, and the field value as method parameters.
  4. Implement the setFieldValue() method. This method gets the target field, ensures that we can access the target field, and sets the field value of the target field.

After we have implemented this utility class, its source code looks as follows:

 1final class ReflectionFieldSetter {
 2
 3    private ReflectionFieldSetter() {}
 4
 5    static def setFieldValue(target, fieldName, fieldValue) {
 6        try {
 7            def targetField = findFieldFromClassHierarchy(target.class, fieldName)
 8            targetField.accessible = true
 9            targetField.set(target, fieldValue)
10        }
11        catch (Exception ex) {
12            throw new RuntimeException(
13                    "Cannot set the field value because of an error",
14                    ex
15            )
16        }
17    }
18
19    private static def findFieldFromClassHierarchy(clazz, fieldName) {
20        Class<?> current = clazz
21
22        def continueSearch = true
23        while (continueSearch) {
24            try {
25                return current.getDeclaredField(fieldName);
26            }
27            catch(Exception e) {
28                //An exception simply means that field is not found.
29            }
30
31            current = current.superclass
32            continueSearch = current != null
33        }
34
35        throw new NoSuchFieldException(String.format(
36                "No field found with the field name %s",
37                fieldName
38        ))
39    }
40}

Second, we have to modify the build() method of the TaskBuilder class. This method must set the field values of the id, creationTime, and modificationTime fields by using our new utility class.

After we have made the required changes to the build() method, the source code of the TaskBuilder class looks as follows:

 1import java.time.ZonedDateTime
 2
 3class TaskBuilder {
 4
 5    private static final NOT_IN_USE = -1L
 6    private static final NOT_IMPORTANT = 'NOT_IMPORTANT'
 7    private static final NOT_SET = ZonedDateTime.now().minusYears(9999)
 8
 9    private assigneeId = null
10    private closerId = null
11    private creationTime = NOT_SET
12    private creatorId = NOT_IN_USE
13    private description = NOT_IMPORTANT
14    private id = NOT_IN_USE
15    private modificationTime = NOT_SET
16    private modifierId = NOT_IN_USE
17    private resolution = null
18    private status = null
19    private tags = new HashSet<>()
20    private title = NOT_IMPORTANT
21
22    //The methods that set field values are omitted
23
24    def build() {
25        def task = Task.builder
26                .withCreator(creatorId)
27                .withDescription(description)
28                .withTitle(title)
29                .build()
30
31        if (assigneeId != null) {
32            task.@assignee = new Assignee(assigneeId)
33        }
34
35        if (closerId != null) {
36            task.@closer = new Closer(closerId)
37        }
38
39        ReflectionFieldSetter.setFieldValue(task,
40				'creationTime',
41				creationTime
42		)
43        ReflectionFieldSetter.setFieldValue(task, 'id', id)
44        ReflectionFieldSetter.setFieldValue(task,
45                'modificationTime',
46                modificationTime
47        )
48
49        if (modifierId != null) {
50            task.@modifier = new Modifier(modifierId)
51        }
52
53        task.@status = status
54        task.@resolution = resolution
55        task.@tags = tags
56
57        return task
58    }
59}

We can now create new objects by using the named argument constructor and set the field values of the created objects by using the syntactic sugar provided by the Groovy programming language.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us five things:

  • Named argument constructors are useful if the created object is just a data container and we want to set only a few field values.
  • When we access a field by using properties, the appropriate getter or setter is invoked transparently.
  • The direct field access operator allows us to access field values without invoking getters or setters.
  • If we want to set the field values of a superclass that has no setters and the fields are private, we have to use reflection.
  • If we want to access the fields of a Java class, we can use either properties or reflection.

Get the source code from Github