Test Driven Development (TDD) is a great technique of coding, where we start with the tests and move forward with the production code. That means, the tests are the driven of our development, not our production code.
In this post, we are going to solve a typical development challenge using TDD:
Transforming an Entity to a DTO.
NOTE: You might think “Come on, that’s a trivial challenge to solve with TDD, let’s talk about something difficult please”. Well, yes, the challenge is trivial, but, as we will see, with a wrong strategy, the triviality could disappear and get us troubles. Moreover, you might have found this challenge in your projects, if not, in which cave do you live??
NOTE2: Why don’t we use a mapper framework like Orika or Dozer? well, I think, transformation is logic, that you should control and test. Of course, the context of the project defines if that logic is important to you or not, so, I suggest checking twice if you really need that kind of framework.
TRY IT YOURSELF: You can find the source code of this post here.
What is TDD?
Test Driven Development is a coding technique where we think first in the unit tests than the production code:

That means, creating the unit tests that fail, then we must create the production code to get those tests pass, and finally refactoring, and do the process again, until your requirements are fulfill.
Transforming an Entity to a DTO
Well, we have a Person entity and a PersonDTO class. The idea is to transform one to another.

Let’s start with the Person entity:
class Person { private String name; private String address; private LocalDate birthDate; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public LocalDate getBirthDate() { return birthDate; } public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name) && Objects .equals(address, person.address) && Objects .equals(birthDate, person.birthDate); } ...... }
Now, let’s see the PersonDTO class:
class PersonDTO { private String name; private String address; private LocalDate birthDate; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public LocalDate getBirthDate() { return birthDate; } public void setBirthDate(LocalDate birthDate) { this.birthDate = birthDate; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PersonDTO person = (PersonDTO) o; return Objects.equals(name, person.name) && Objects .equals(address, person.address) && Objects .equals(birthDate, person.birthDate); } .... }
TIP: Those classes look pretty similar each other. That’s a typical use case in our projects, we create data structures like DTO to decoupling the Entity from other layers like the view.
And finally, we create a PersonTransformer class to do the mapping:
class PersonTransformer { public PersonDTO transform(Person person){ PersonDTO personDTO = new PersonDTO(); personDTO.setAddress(person.getAddress()); personDTO.setBirthDate(person.getBirthDate()); personDTO.setName(person.getName()); return personDTO; } }
Of course, we create unit tests for this transformer:
public class PersonTransformerTest { private PersonTransformer personTransformer = new PersonTransformer(); @Test public void transformFromPersonToPersonDTO(){ Person person = new Person(); person.setAddress("Street 1"); person.setBirthDate(LocalDate.of(1980, 7, 4)); person.setName("Daniel"); PersonDTO personDTOExpected = new PersonDTO(); personDTOExpected.setAddress("Street 1"); personDTOExpected.setBirthDate(LocalDate.of(1980, 7, 4)); personDTOExpected.setName("Daniel"); PersonDTO personDTO = personTransformer .transform(person); assertThat(personDTO).isEqualTo(personDTOExpected); } }
When we run the tests, everything looks fine:

Now, we have a new requirement from our customer.
TRY IT YOURSELF: You can find the source code of this section here.
A New Requirement: Add a phone
property
Our customer wants to see the phone
number of the person, that means, we need to modify our current code to add the new phone
field.
We are going to apply two strategies to code this new requirement:
- No TDD strategy: We are going to add the field directly in the production code, thinking later about tests.
- TDD strategy: We are going to start modifying the tests to fit the new requirement, and the production code when is required.
No TDD Strategy
Well, let’s see what we usually do if we do not apply TDD.
1. First, we add the new phone
property with its setter and getter method, to the Person entity.
class Person { private String name; private String address; private LocalDate birthDate; private String phone; .... public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } .... }
2. Next, we add the new phone
property with its setter and getter method, to the PersonDTO class.
class PersonDTO { private String name; private String address; private LocalDate birthDate; private String phone; .... public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } .... }
3. Now, we add the transformation missing in the PersonTransformer class.
class PersonTransformer { public PersonDTO transform(Person person){ PersonDTO personDTO = new PersonDTO(); personDTO.setAddress(person.getAddress()); personDTO.setBirthDate(person.getBirthDate()); personDTO.setName(person.getName()); personDTO.setPhone(person.getPhone()); return personDTO; } }
4. Finally, we run our unit tests to see that everything is fine.

Awesome, our unit tests passed without problem…… wait what??? we just added logic to our application and none unit test breaks, something is missing…. but, I am in a hurry, I won’t lose time checking the unit tests and see what is going on, at the end, they passed on green, so, my CI tool won’t break, and that’s the important part.
NOTE: If none unit tests failed, that means, we do not cover that part of the code with tests, and makes sense, because, we are adding code, so, that new code is not cover because doesn’t exist before. If we really concern about this, we should go to check what we are missing in the unit tests.
NOTE2: Note that when I said “cover”, I am not referring to test coverage, I am referring to code that is really tested. Those two are different things we can discuss in future posts.
TRY IT YOURSELF: You can find the source code of this section here.
A TDD approach
Well, let’s see how this problem is solved using TDD.
1. First, add the new phone
field to the PersonTransformerTest.
package com.coderstower.blog.a_tdd_case_transforming_between_objects.addfield.tdd.step1; import org.junit.Test; import java.time.LocalDate; import static org.assertj.core.api.Assertions.assertThat; public class PersonTransformerTest { private PersonTransformer personTransformer = new PersonTransformer(); @Test public void transformFromPersonToPersonDTO() { Person person = new Person(); person.setAddress("Street 1"); person.setBirthDate(LocalDate.of(1980, 7, 4)); person.setName("Daniel"); person.setPhone("111111"); PersonDTO personDTOExpected = new PersonDTO(); personDTOExpected.setAddress("Street 1"); personDTOExpected .setBirthDate(LocalDate.of(1980, 7, 4)); personDTOExpected.setName("Daniel"); personDTOExpected.setPhone("22222"); PersonDTO personDTO = personTransformer .transform(person); assertThat(personDTO).isEqualTo(personDTOExpected); } }
NOTE: did you notice that the phones’ values for Person entity and PersonDTO don’t match? well, that is intentional, we shouldn’t solve the problem at the first try. TDD is a process that builds a solution in an incremental and iterative way.
Of course, this code doesn’t compile, the method setPhone
doesn’t exist in the Person entity nor PersonDTO class. If we try to run that test, we get:

2. Now, we fix the compilation error. First, we go to Person entity and add the new setPhone method with its property.
class Person { private String name; private String address; private LocalDate birthDate; private String phone; .... public void setPhone(String phone) { this.phone = phone; } .... }
And we add the property to PersonDTO too.
class PersonDTO { private String name; private String address; private LocalDate birthDate; private String phone; .... public void setPhone(String phone) { this.phone = phone; } }
If we run the unit tests, we get now this:

3. We shouldn’t get green tests, so, we are missing something. Let’s see carefully our unit tests:
public class PersonTransformerTest { private PersonTransformer personTransformer = new PersonTransformer(); @Test public void transformFromPersonToPersonDTO() { Person person = new Person(); .... PersonDTO personDTOExpected = new PersonDTO(); .... PersonDTO personDTO = personTransformer .transform(person); assertThat(personDTO).isEqualTo(personDTOExpected); } }
The assert uses isEqualTo
method, which uses the equals method for our classes to compare, so, we need to add our new field to the equals method. First in the Person entity:
class Person { private String name; private String address; private LocalDate birthDate; private String phone; .... public void setPhone(String phone) { this.phone = phone; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name) && Objects .equals(address, person.address) && Objects.equals(birthDate, person.birthDate) && Objects.equals(phone, person.phone); } .... }
And in the PersonDTO:
class PersonDTO { private String name; private String address; private LocalDate birthDate; private String phone; .... public void setPhone(String phone) { this.phone = phone; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PersonDTO personDTO = (PersonDTO) o; return Objects .equals(name, personDTO.name) && Objects.equals(address, personDTO.address) && Objects.equals(birthDate, personDTO.birthDate) && Objects.equals(phone, personDTO.phone); } .... }
Now, let’s try the tests again:

Awesome, the test failed. We are sending “222222” and “111111” in the test, however, we are getting a failure by a null
phone. So, we are missing the transformation.
4. Adding the transformer code for phone
field.
class PersonTransformer { public PersonDTO transform( Person person){ PersonDTO personDTO = new PersonDTO(); personDTO.setAddress(person.getAddress()); personDTO.setBirthDate(person.getBirthDate()); personDTO.setName(person.getName()); personDTO.setPhone(person.getPhone()); return personDTO; } }
However, we need to add the phone
getter in the classes too. First for the Person entity:
class Person { private String name; private String address; private LocalDate birthDate; private String phone; .... public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return Objects.equals(name, person.name) && Objects .equals(address, person.address) && Objects.equals(birthDate, person.birthDate) && Objects.equals(phone, person.phone); } .... }
And for PersonDTO:
class PersonDTO { private String name; private String address; private LocalDate birthDate; private String phone; .... public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PersonDTO personDTO = (PersonDTO) o; return Objects .equals(name, personDTO.name) && Objects.equals(address, personDTO.address) && Objects.equals(birthDate, personDTO.birthDate) && Objects.equals(phone, personDTO.phone); } .... }
Finally, if we run the tests:

Good, now we are not getting a null
phone, we are getting the value we are sending on the unit test.
5. Fix the unit test data.
public class PersonTransformerTest { private PersonTransformer personTransformer = new PersonTransformer(); @Test public void transformFromPersonToPersonDTO() { Person person = new Person(); person.setAddress("Street 1"); person.setBirthDate(LocalDate.of(1980, 7, 4)); person.setName("Daniel"); person.setPhone("111111"); PersonDTO personDTOExpected = new PersonDTO(); personDTOExpected.setAddress("Street 1"); personDTOExpected .setBirthDate(LocalDate.of(1980, 7, 4)); personDTOExpected.setName("Daniel"); personDTOExpected.setPhone("111111"); PersonDTO personDTO = personTransformer .transform(person); assertThat(personDTO).isEqualTo(personDTOExpected); } }
There, we change the “222222” by “111111” phone in the expected person, so, when we run the unit test, we get:

And we completed the TDD process and our requirement is fulfill.
TRY IT YOURSELF: You can find the source code of this section here.
Final Thought
We just compare the no TDD process with a TDD strategy, and as we can see, there were only one step more in the TDD strategy, however, if you paid attention, those additional steps were necessary, otherwise, our code will be incomplete, as it was in the no TDD approach.
TDD helps us to have a good unit tests set and minimal production code that fulfill our requirements.
If you liked this post and are interested in hearing more about my journey as a Software Engineer, you can follow me on Twitter and travel together.
Excelente Post, muy claro todo. Gracias!
LikeLike
Buenas, seria muy bueno tener un post sobre Mocks, cuando si, cuando no y buenas practicas.
LikeLike
Si, es buena idea, por ahi la tengo en mis pendientes
LikeLike
[…] NOTE: You can see a good example of why to use TDD in the post A Case in Favor of TDD: Transforming an Entity to DTO […]
LikeLike