This article is the 17th day of Android 2 Advent Calendar 2016 --Qiita.
Today, as one of the useful patterns for maintaining test code, I would like to introduce the ** Test Data Builder pattern **.
During actual development
I think you often write test code for classes that use models (such as value objects).
This time, assuming such a case, the value object is ʻUser, and the class using it is ʻUserRepository
.
Let's proceed with the story as a sample.
User.java
//model
final class User {
private final long id;
private final String name;
private final int age;
private final Gender gender;
private final String avatarUrl;
private final Email email;
User(long id, String name, int age, Gender gender,
String avatarUrl, Email email) {
this.id = id;
this.name = name;
this.age = age;
this.gender = gender;
this.avatarUrl = avatarUrl;
this.email = email;
}
}
UserRepository.java
//Class using a model
class UserRepository {
User findBy(long id) {
return null; // TODO
}
List<User> findAll() {
return null; // TODO
}
void store(User user) {
// TODO
}
}
When I try to write this Repository
test, I think it looks like this:
UserRepository.java
public class UserRepositoryTest {
private UserRepository repo;
@Before public void setUp() {
repo = new UserRepository();
}
@Test public void store() {
long id = 100;
User expected = new User(id, "", 1, Gender.MALE, "", new Email(""));
repo.store(expected);
User actual = repo.findBy(id);
assertThat(actual, is(equalTo(expected)));
}
@Test public void findAll() {
repo.store(new User(1, "", 1, Gender.MALE, "", new Email("")));
repo.store(new User(2, "", 1, Gender.MALE, "", new Email("")));
List<User> actual = repo.findAll();
assertThat(actual.size(), is(2));
}
}
I think the problem with this test code is the ** ʻUser` class instantiation part **. Here's why.
--ʻ The code is hard to see because there are many arguments in the constructor of the Userclass. --Many descriptions have no meaning such as
1 and
"" ―― It is difficult for others to understand which variable in the User
class is the focus of the test.
――What is important is ʻid? Or is it all? --ʻ ʻUser
class constructor is vulnerable to change
--The test cannot be executed without modifying all the test code that uses the constructor.
Both can be a liability for long-term operation of the test code.
To address the above issues, we will prepare a class that specializes in creating data for the ʻUser` class for testing. This is called the ** Test Data Builder ** pattern.
The point is that ** the member variables of this class have default values set **.
UserDataBuilder.java
class UserDataBuilder {
private long id = 1;
private String name = "name";
private int age = 20;
private Gender gender = Gender.MALE;
private String avatarUrl = "avatarUrl";
private Email email = new EmailDataBuilder().build();
public User build() {
return new User(id, name, age, gender, avatarUrl, email);
}
public UserDataBuilder withId(long id) {
this.id = id;
return this;
}
public UserDataBuilder withName(String name) {
this.name = name;
return this;
}
...abridgement...
public UserDataBuilder withEmail(Email email) {
this.email = email;
return this;
}
}
Using this ʻUserDataBuilder` class, if you modify the test code above, it will be as follows.
UserRepositoryTest.java
public class UserRepositoryTest {
private UserRepository repo;
@Before public void setUp() {
repo = new UserRepository();
}
@Test public void store() {
long id = 100;
User expected = new UserDataBuilder().withId(id).build();
repo.store(expected);
User actual = repo.findBy(id);
assertThat(actual, is(equalTo(expected)));
}
@Test public void findAll() {
UserDataBuilder user = new UserDataBuilder();
repo.store(user.withId(1).build());
repo.store(user.withId(2).build());
List<User> actual = repo.findAll();
assertThat(actual.size(), is(2));
}
}
What do you think. Compared to the test code that does not use patterns, this code is
I think there is such a merit.
The ʻUser` class used in this example was a relatively easy object to instantiate. However, in actual development, there are many dependent classes, and there are many cases where it is difficult to prepare test data. At that time
To write the test code Why not apply this ** Test Data Builder pattern **?
Recommended Posts