Back to feed
Renewal·서른의 생활코딩

TDD Practice in Spring Boot - Ch.3. Equality for All

NS
normalstory
cover image

STS 4.0.1 (Spring Boot): 

Practicing TDD (Test Driven Development)

 

Opening noteMost people can make the most of their potential by following these two simple rules:

1. Before writing any code, write a failing automated test.

2. Remove duplication.


  Requirements and improvements list

       - $5 + 10CHF = $10 (if the exchange rate is 2:1)

       - $5 x 2 = $10 

- Make amount private 

- Dollar side effects?  

- Money rounding? 

- New: equals( )     Implement the equality feature    <- Goal for this example 

-New: hashCode( ) 

-New: Equal null

-New: Equal object

  Purpose. Understanding the rhythm of TDD

1. Quickly add a single test.

2. Run all tests and confirm the new one fails.

3. Make a small change to the code.

4. Run all tests and confirm they all pass.

5. Refactor to remove duplication.



A leftover note from the previous chapter 

Turning a feeling (a disgust toward a side effect) into a test (multiplying on a single Dollar object twice) is a general theme of TDD. 



Chapter 3. Equality for All 


3-0. Recognizing the problem 

 1) The worst bug in the author's experience was 

that changing the value on the second check also changed the value on the first — an aliasing problem. 

To fix this, you need to use value objects. One of the constraints of a value object is that the object's instance variables, once set in the constructor, never change — so if you use value objects, you don't need to worry about aliases. ( The "alias" the author mentions I take to mean variable or method names. )


2) But actually, this way of using an object as a value — like the Dollar object we wrote — is the Value Object Pattern

A Value Object is conceptually a very small and simple object. But it's an essential element of Domain-Driven Design and object-oriented programming. Value objects are defined by the rule that the equality of two value objects is not based on identity, but on their content. (the equality of two Value Objects is not based on identity) 


Equality has two operators: '==' and '===.' '==' is for equality testing, and on scalar values, strings, or integers, it checks whether the values are the same. '===' on the other hand, when used on two objects, tells you whether the two objects are actually the same. Two objects being "the same" means they share the same memory location — so if one of them is changed by some operation, the other is affected too.

Source: A Penguin Living in an Igloo 


2) In short, the goal for this chapter is to implement the equality feature so we can compare Dollar to Dollar directly. Along the way we'll use the triangulation strategy.

Triangulation: if a radio signal is being detected at two receivers, the distance between the receivers is known, and the direction of the signal at each receiver is known, that's enough information to figure out the distance and bearing of the signal.


3-1. Run the test first.


package com.noramlstory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTdd2019 {
	
	@Test
	public void testMultiplication() {
		Dollar five = new Dollar(5);
		Dollar product; 
		
		product = five.times(2);
		assertEquals(10, product.amount);
		
		product = five.times(3);
		assertEquals(15, product.amount);
	}
	
	// 3장. 모두를 위한 평등 
	@Test	
	public void testEquality() {
		// 3-1. 일단 테스트를 진행한다.  $5 == $5    ( $5 같다 $5 )
		assertTrue(new Dollar(5).equals(new Dollar(5)));
	}
	
}

class Dollar{
	int amount;
	
	Dollar(int amount){
		this.amount = amount;
	}
	
	Dollar times(int multiplier) {
		return new Dollar(amount * multiplier);
	}
}



3-2 Make it work, even if you have to fake it.


package com.noramlstory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTdd2019 {
	
	@Test
	public void testMultiplication() {
		Dollar five = new Dollar(5);
		Dollar product; 
		
		product = five.times(2);
		assertEquals(10, product.amount);
		
		product = five.times(3);
		assertEquals(15, product.amount);
	}
	
	// 3장. 모두를 위한 평등 
	@Test	
	public void testEquality() {
		// 3-1. 일단 테스트를 진행한다.
		assertTrue(new Dollar(5).equals(new Dollar(5)));
	}
	
}

class Dollar{
	int amount;
	
	Dollar(int amount){
		this.amount = amount;
	}
	
	Dollar times(int multiplier) {
		return new Dollar(amount * multiplier);
	}
	
	// 3-2 일단 가짜로 라도, 돌아가게 만든다.
	public boolean equals(Object object) {
		return true;
	}
}



3-3 Adding an opposite comparison for triangulation..


package com.noramlstory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTdd2019 {
	
	@Test
	public void testMultiplication() {
		Dollar five = new Dollar(5);
		Dollar product; 
		
		product = five.times(2);
		assertEquals(10, product.amount);
		
		product = five.times(3);
		assertEquals(15, product.amount);
	}
	
	// 3장. 모두를 위한 평등 
	@Test	
	public void testEquality() {
		// 3-1. 일단 테스트를 진행한다.  $5 == $5    ( $5 같다 $5 )
		assertTrue(new Dollar(5).equals(new Dollar(5)));

		// 3-3. 삼각 측량을 위해 반대? 비교를 하나 추가 해본다.    $5 != $6   ( $5 같지않다 $6 )
		assertFalse(new Dollar(5).equals(new Dollar(6)));
	}
	
}

class Dollar{
	int amount;
	
	Dollar(int amount){
		this.amount = amount;
	}
	
	Dollar times(int multiplier) {
		return new Dollar(amount * multiplier);
	}
	
	// 3-2 일단 가짜로 라도, 돌아가게 만든다.
	public boolean equals(Object object) {
		return true;
	}
}



3-4 Generalize the equality. -> Implement the equality feature via the  equals() method 


package com.noramlstory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestTdd2019 {
	
	@Test
	public void testMultiplication() {
		Dollar five = new Dollar(5);
		Dollar product; 
		
		product = five.times(2);
		assertEquals(10, product.amount);
		
		product = five.times(3);
		assertEquals(15, product.amount);
	}
	
	// 3장. 모두를 위한 평등 
	@Test	
	public void testEquality() {
		// 3-1. 일단 테스트를 진행한다.  $5 == $5    ( $5 같다 $5 )
		assertTrue(new Dollar(5).equals(new Dollar(5)));

		// 3-3. 삼각 측량을 위해 반대? 비교를 하나 추가 해본다.    $5 != $6   ( $5 같지않다 $6 )
		assertFalse(new Dollar(5).equals(new Dollar(6)));
	}
	
}

class Dollar{
	int amount;
	
	Dollar(int amount){
		this.amount = amount;
	}
	
	Dollar times(int multiplier) {
		return new Dollar(amount * multiplier);
	}
	
	// 3-2 일단 가짜로 돌아가게 만든다.
        //	public boolean equals(Object object) {
        //		return true;
        //	}
	
	// 3-4 동치성을 일반화 한다 
	public boolean equals(Object object) {
		Dollar dollar = (Dollar) object;
		return amount == dollar.amount;
	}
}



3-5 What changed compared to the previous example, with some personal commentary. 

Before, we compared the expected value with Dollar(5)       

 assertEquals(10 , product.amount);

Today, we compared Dollar(5) with Dollar(n).    

 assertTrue(new Dollar(5) .equals(new Dollar(5)));


The fancy? word "equivalence" came out, but in the end it's a term introduced to distinguish living in the same place == from being the same entity ===.

The former is when the address (the variable) is the same; the latter means the address and the value are the same.

Something like the difference between people who share a name and the same person, maybe? 

That's a good way to think about it.



Quick detour ~ ;D 

There's a similar example in Spring — leaving a link for reference ~

-> Following Toby's Spring 3.1 in Spring Boot: Ch.1 - 1.6 Singleton Registry and Object Scope 



This English version was translated by Claude.

친절한 찰쓰씨
Written by
친절한 찰쓰씨

Pleasant Charles — UI/UX researcher at AIT. Keeping notes on design, planning, and slow days here since 2010.

More on the author's page

Keep reading

Renewal

Steadily, for the long haul, without burning out

Mar 31, 2026·9 min
Renewal

Tech-life balance

Feb 7, 2026·3 min
Renewal

Humanality, by Park Jeong-ryeol

Feb 7, 2026·11 min