본문 바로가기
  • think normal
새로워지기/서른의 생활코딩

스프링 부트에서 테스트 주도 개발 실습 - 3장. 모두를 위한 평등

by 청춘만화 2019. 2. 23.

STS 4.0.1(스프링 부트)에서 

TDD(테스트 주도 개발 Test Driven Development) 실습하기

 

들어가는 글대다수의 사람들은 다음 두 가지 단순한 법칙을 따름으로써 잠재력을 한껏 발휘할 수 있다.

1. 어떤 코드건 작성하기 전에 실패하는 자동화된 테스트를 작성하라.

2. 중복을 제거하라.


  요구사항 및 개선사항 목록

       - $5 + 10CHF = $10 (환율이 2:1일 경우)

       - $5 x 2 = $10 

- amount를 private로 만들기 

- Dollar 부작용(side effect)?  

- Money 반올림? 

-신규 : equals( )    동질성 기능을 구현    <- 이번 예제의 목표 

-신규 : hashCode( ) 

-신규 : Equal null

-신규 : Equal object

  목적. 테스트 주도 개발의 리듬 이해

1. 재빨리 테스트를 하나 추가한다.

2. 모든 체스트를 실행하고 새로 추가한 것이 실패하는 지 확인한다

3. 코드를 조금 바꾼다.

4. 모든 테스트를 실행하고 전부 성공하는지 확인한다.

5. 리팩토링을 통해 중복을 제거한다.



지난 장에 못다 남긴 글 

느낌( 부작용에 대한 혐오감)을 테스트(하나의 Dollar 객체에 곱하기를 두번 수헹하는 것)로 변환하는 것은 TDD의 일반적인 주제이다. 



3장. 모두를 위한 평등 


3-0. 문제 인식 

 1) 저자의 경험 중 가장 형편없는 버그는 

두번째 수표의 값을 변화시키는 바람에 첫번째 수표의 값까지 변하게 되는 문제이다. 별칭의 문제이다. 

이를 개선하기 위해서는 값 객체를 사용해야한다. 값 객체의 제약 중 하나가 객체의 인스턴스 변수가 생성자를 통해 일단 설정된 후에는 결코 변하지 않기 때문에 값 객체를 사용하면 별칭에 걱정할 필요가 없다. ( 저자가 말하는 별칭은 변수 또는 메서드 이름을 말하는 것으로 생각된다. )


2) 그런데 사실, 우리가 작성한 Dollar 객체같이 객체를 값처럼 쓸 수 있는 것을 값 객체 패턴 Value Object Pattern 이라고 한다. 

값 객체(Value Object)는 개념적으로는 아주 작고 단순한 객체이다. 하지만 도메인 지향 디자인(Domain-Driven Design)과 객체지향 프로그래밍의 필수적인 요소이다. 값 객체는 두개의 값 객체의 동일성(equality) 은 Identity에 기반하지 않고 그들의 컨텐츠로 한다로 정의된다. (the equality of two Value Object is not based on identity) 


동일성 에 대한 연산자가 두개 있다. '==' 와 '===' 이다. '=='는 equality 테스팅을 위한 것인데 스칼라(scalar)나 문자열 혹은 정수형에 값이 같은지를 체크하는 연산자이다. 반면에 '==='는 두 객체를 사용하고 있을때에 두 객체가 실제로 같은지에 대한 것을 판별해준다. 두 객체가 같다는 말은 같은 메모리 위치를 공유한다는 것이고 한쪽 객체에서 어떤 연산으로 객체의 값을 바꾸면 다른 객체에도 영향을 미친다는 뜻이다.

출처 : 이글루에 사는 펭귄 


2) 요컨데, 이 장에서 얻고자 하는 목표는 동질성 기능을 구현하여 Dollar와 Dollar를 직접 비교하는 것이다. 이 과정에서 삼각측량전략을 사용한다.

삼각측량이란 만약 라디오 신호를 두 수신국에서 감지하고 있을 때, 수신국 사이의 거리가 알려져있고 각 수신국의 신호 방향을 알고 있다면 이 정보만으로 충분히 신호의 거리와 방위를 알 수 있다.


3-1. 일단 테스트를 진행한다.


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 일단 가짜로 라도, 돌아가게 만든다.


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 삼각 측량을 위해 반대? 비교를 하나 추가 해본다.


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 동치성을 일반화 한다. -> equals() 메소드를 통해  동질성 기능 구현 


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 오늘 예제가 지난 예제와 달라진 점은? 또는 개인적인 해설 

이전에는 기대 값과 Dollar(5)와 비교했다면       

 assertEquals(10 , product.amount);

오늘은 Dollar(5)와 Dollar(n)을 비교했다는 점이다.    

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


동치라는 어려운? 단어가 나왔지만 결국 같은 곳에 사는가 == , 같은 존재인가 === 를 구분하기 위해 나온 용어이다.

전자는 변수라는 주소가 같은 곳에 담겨 있는 상태이고 후자는 주소과 값까지 같은 것을 말한다.

이를테면 동명인동일인 의 차이 정도? 

라고 이해하면 좋을 것 같다.



여기서 잠깐~ ;D 

스프링에서도 유사한 예제가 있어 링크를 걸어둡니다~

-> 스프링 부트에서 토비 3.1 따라하기 : 1장 - 1.6 싱글톤 레지스트리와 오브젝트 스코프 



댓글