🧪 Test Driven Development

Kiwoong Kwon

2019. 10. 19.

Chapter 1 ~ 3

Table Of Contents

  1. Prerequisites

    1. eXtreme Programming

    2. Terminologies

    3. TDD Discipline

  2. Into the book

    1. 다중 통화를 지원하는 Money 객체

    2. 타락한 객체

    3. 모두를 위한 평등

      🧪 Prerequisites

TDD 

이 영상에서 무엇이 가장 황당하셨나요?

      🧪 Prerequisites

정답은 테스트 없이 구구클래스를 코딩했다는 것입니다

(혹시 테스트 짜서 코드가 무거워질걸 대비한건...?)

      🧪 Prerequisites

Q: 아니 구구단 프로그램 같은것도 테스트가 필요하다구요?

A: 그럼요. "테스트 주도 개발" 에서는 말이죠

      🧪 eXtreme Programming

그럼 TDD 는 어디서 왔느냐...

태초에 eXtreme Programming 이 있었다

      🧪 eXtreme Programming

XP가 하고싶은 것? 고객에게 가치를 전달하는 것!

TDD, Pair Programming, Stand up, CI/CD 는 실천방법에 불과

TDD

      🧪 Terminologies

Software Lifecycle (Cascade model)

      🧪 Terminologies

Software Lifecycle (Prototype model)

      🧪 Terminologies

소프트웨어 테스트는 SDLC 종단에서

"설계한 대로 동작하는지" 를 확인하는 작업

 

회사에서는 QA/QC 작업에 해당

      🧪 Terminologies

소프트웨어 테스트 또한 하나의 업무 분야로

배경 지식으로 알아야 할 용어들이 있습니다 😑

      🧪 Terminologies

너무 많으니까 필요한 것만...

      🧪 Terminologies

📖 Acceptance Testing

실제 사용자 환경에서, 사용자에 빙의 하여 테스트를 수행 함.

사용자가 만족할만한 제품으로 인수(Accept) 할 의향이 있는지를

요구사항 명세를 통해 테스트

: 인수 테스트

      🧪 Terminologies

📖 Assertion

단언문이 선언된 지점에서 반드시 참(true) 이어야 한다고 생각하는

사항을 표현한 논리식이다. 단언문에 위반되는 경우는 프로그램에

버그가 있다고 간주한다

: 단언

      🧪 Terminologies

📖 Behavior Testing

테스팅 기술 중 테스트 더블 (뒤에서 설명) 과 협력하여

원하는 method 정확한 순서로 정확한 인자와 함께 호출 하였는지를

검증한다

: 행위 검증

      🧪 Terminologies

📖 Behavior-Driven Development (BDD)

TDD의 subset 으로, 요구사항.equals(테스트케이스) 가 된다는

점을 핵심개념으로 한다. 기술적인 지식이 없는 관계자 또한 테스트

케이스에 사용될 사양을 작성하거나 읽을 수 있어야 한다

: 행위 주도 개발

      🧪 Terminologies

📖 Black-Box Testing

소프트웨어 검사 방법 중 하나로 어떤 소프트웨어 내부 구조나 작동

원리를 모르는 상태에서 소프트웨어 동작을 검사하는 방법이다

: 블랙박스 테스트

      🧪 Terminologies

📖 Dummy

더-미 는 테스트더블의 타입 중 하나로, 실제 프로그램에서는

사용되지 않고, 오직 테스트 환경에서 요구되는 파라미터를 채우기

위함을 목적으로 사용된다

: 더-미 (가짜)

      🧪 Terminologies

📖 Fake

훼이크는 테스트 더블로 테스트 환경에서만 사용하는 가짜 구현을

의미합니다. 예를 들어 인-메모리 테스트 데이터베이스 구축을 하는것

또한 페이크의 일종입니다

: 훼이크

      🧪 Terminologies

      🧪 Terminologies

📖 Fixture

테스트 실행 전 세팅되어 있어야 하는 특정 환경

일반적으로 테스트에 사용될 테스트 더블을 미리 구성한다

: 픽스쳐

      🧪 Terminologies

📖 Functional Testing

고차원의 테스트로 비즈니스 요구사항을 확인합니다

기능 테스트는 보통 유저 스토리(뒤에 나옴)을 포함하여 가능한

많은 사용자 시나리오를 포함하여 테스트 합니다

: 함수형 기능 테스트

# In this example we check that the about page of the website is working as expected
open 'example.com'
clickOn 'about us'
assertThereIs 'We are a small Example company'

      🧪 Terminologies

📖 Green

통과하는 테스트 모음 또는 특정 통과하는 테스트를 일컫는 말

: 그-린

      🧪 Terminologies

📖 Integration Testing

Functional Testing > Integration Testing > Unit Testing

중간 단계의 테스트 활동으로 여러 하위 모듈 또는 시스템이

유기적으로 동작 하는지를 확인하는 테스트 입니다.

 

모듈/시스템 간 인터페이스와 데이터 흐름에 초점을 맞추는 테스트

: 통합 테스트

# In this example we check that the newly registered user,
# who was referred by another user, gets an on-site "friendship" created.
# Here we check the interaction between the form controller,
# database, and a User active record
db = new Fake Db
u1 = db.createUser(name='john')
RegistrationForm(db, name='kate', referred='john').save()
assert(u1.hasFriend('kate'))

      🧪 Terminologies

📖 Mock

테스트 더블(뒤에 나와여;) 타입 중 하나로 특정한 테스트를 위해

생성됩니다. 아까 나왔던 행위 검증을 위해 Mock 객체가 사용됨

: 모-크 (목)

public class EmailServiceMock extends EmailSevice {
    private boolean isSendMailCalled = false;
    private String message = "";
    
    @Override
    public void sendMail(String message) {
        isSendMailCalled = true;
        message = message;
    }
    
    public boolean isSendMailCalled() {
        return isSendMailCalled
    }
    
    public String getMessage() {
        return message;
    }
}

public class EmailTest {
    
    @Test
    public void testSendEmail() throws Exception {
        final String message = "이-메일 내용";
        
        // Given: Create Mock Email Service
        EmailServiceMock emailServiceMock = new EmailServiceMock();
        EmailSender emailSender = new EmailSender(emailServiceMock);
        
        // When: Send E-mail
        emailSender.send(message);
        
        // Then - sendEmail is called with our message
        Assert.assertTrue(emailServiceMock.isSendMailCalled());
        Assert.assertEquals(message, emailServiceMock.getMessage());
    }
}

      🧪 Terminologies

📖 Monkey-Patching

기존 객체 또는 클래스의 동작을 확장하거나 변경하는 방법입니다

몽키패칭은 Dependency Injection 과 테스트 더블의 대안입니다

: 몽키패칭

# In this example we replace the standard library function
# to prevent the test from using a real filesystem
import sys

sys.path = ["foo", "bar"]
assertContains("bar", sys.path)

      🧪 Terminologies

📖 Red

실패하는 테스트 집합 또는 특정 테스트를 지칭 함

: 🚨 레-드

      🧪 Terminologies

📖 Refactoring

리팩토링은 더 나은 구현을 위해 코드를 변경하는 것 입니다

"외부 동작을 바꾸지 않으면서 내부 동작을 개선" 하는 것이

가장 중요합니다

 

(근데 어차피 안 하잖아? 안될거야 아마...)

: 리팩토링 (a.k.a. 말만 하는 거)

      🧪 Terminologies

📖 Regression

어떤 프로그램이 수정된 뒤에 해당 프로그램이 제대로 동작

하는지를 확인하기 위해 수행하는 테스트를 의미합니다

 

또는 프로그램 수정 후 과거에 수정한 버그가 다시 살아나는

"회귀 버그" 를 찾아 동작을 보장하는 테스트 또한 의미합니다

: 회귀 테스트

      🧪 Terminologies

📖 Setup

Fixture (픽스쳐) 를 준비하는 과정을 의미합니다

: 셋-업

# In this example we prepare a fake database with some fake values
# that we will need across multiple tests
db = new Fake Db
db.createUser(name='john')
db.createUser(name='kate')
db.createFriendship('john', 'kate')

      🧪 Terminologies

📖 Stub

테스트 더블 중 하나로, stub 을 호출한 쪽에 canned answer

를 제공합니다. canned answer 사전에 약속된 값을 의미

 

예를 들어, 면접 질문을 미리 알려주고 답을 하는 것도

일종의 canned answer 라 할 수 있음

(출처 - https://bit.ly/2Nedp4Q)

: 스텁

 (function () {
 	console.log(“== 테스트 케이스 — 결제가 성공한 경우”);
 	
 // Given
 // Sinon 을 사용하여 Stub 처리
 var stub = sinon.stub(PaymentGateway.prototype, ‘requestPayment’);
 // canned answer
 stub.withArgs(‘테스트 상품’, 2000).returns(‘OK’);
 
 // When
 var order = new Order(‘테스트 상품’, 2000);
 var result = order.pay();
 
 // Then
 if(result == true) {
 	console.log(“> 테스트 성공”);
 } else {
 	console.log(“> 테스트 실패”);
 }
 
 stub.restore();
})();

(function () { 
	console.log(“== 테스트 케이스 — 결제가 실패한 경우”);
	
 // Given
 // Sinon 을 사용하여 Stub 처리
 var stub = sinon.stub(PaymentGateway.prototype, ‘requestPayment’);
 // canned answer
 stub.withArgs(‘테스트 상품’, 0).returns(‘ERROR’);
 
 // When
 var order = new Order(‘테스트 상품’, 0);
 var result = order.pay();
 
 // Then
 if(result == false) {
 	console.log(“> 테스트 성공”);
 } else {
 	console.log(“> 테스트 실패”);
 }
 
 stub.restore();
})();

      🧪 Terminologies

📖 Teardown

Setup 에서 만든 픽스쳐를 정리합니다

GC 를 자동으로 수행하는 언어의 대부분은 Teardown 을

자동으로 수행하도록 합니다

: 티-어 다운 (분해, 해체)

      🧪 Terminologies

📖 Test

요구사항을 확인하기 위한 가능한 가장 작은 단위

: 테스트 (테스트케이스와 다르다)

      🧪 Terminologies

📖 Test Case

테스트의 집합

: 테스트케이스

      🧪 Terminologies

📖 Test Coverage

테스트 수행 결과를 정량적인 수치로 나타내는 방법으로

소스 코드 (테스트 대상: SUT) 중 테스트를 통해 실행된 코드

비율을 뜻합니다

 

구문, 조건, 결정 모두를 테스트 해야 100%를 얻습니다

: 테스트 커버리지

      🧪 Terminologies

📖 Test Cycle

TDD 의 과정입니다. 레드 - 그린 - 리팩터 과정을 의미합니다

: 테스트 순환

      🧪 Terminologies

📖 Test Double

테스트는 독립성을 유지해야 합니다. 실행 순서 또는

다른 테스트에 의존해서는 안되는데, 이와 같은 상황을 위해

의존성으로 부터 격리 시키고, 의존성을 대체 하는 것을

의미합니다.

 

Stub/Mock/Fake/Dummy/Spy 등이 테스트 더블입니다

(더블은 "대역" 이라는 뜻이 있습니다)

: 테스트 더블 (드디어 나옴)

      🧪 Terminologies

📖 Test Suite

Testcase 의 집합

: 테스트 스위트

      🧪 Terminologies

📖 Unit Testing

가장 낮은 단계의 테스트로 테스트 가능한 가장 작은 단위의

코드를 테스트 합니다. 하나의 단위 테스트는 하나의 행위만을

검증하고, 단위 테스트 케이스(Unit test case) 는 함수 또는

클래스의 모든 functionality 를 검증합니다

: 단위 테스트

      🧪 Terminologies

📖 User Story

특정한 목표를 달성하기 위해 특정한 작업을 수행하는

특정한 그룹의 사람들에 대한 설명을 의미합니다

 

유저 스토리는 상세 구현에 대한 설명을 피하고

사람이 읽을 수 있는 기술적 내용을 배제한 (기술적이면 사람은 못 읽는건가...?)

간단한 몇 가지 단어를 사용해 작성됩니다

: 유저(악마) 스토리

As a user, I want to be able to find my friends on this website by my address book, instead of looking for them one by one, because that will save me a lot of time.

      🧪 Terminologies

📖 White-Box Testing

설계 단계에서 요구된 사항을 이미 아는 상태로 개발자 관점

에서 확인하는 테스팅 기법입니다. 커버리지 또한 확인합니다

 

검증 방법으로는 문장/선택/경로/조건 을 사용합니다

: 화이트 박스 테스팅

      🧪 TDD Discipline

~ TDD 훈련법 ~

 

매를 맞아봐야 아픈걸 안다

      🧪 TDD Discipline

      🧪 TDD Discipline

테스트가 주는 안정감이 있습니다

개인 프로젝트야 고장나면 고치면 되지만

당장 내 실수로 몇 천만원이 오간다면? 테스트 없이는 라이브 배포도 없다 😭

      🧪 TDD Discipline

필요성을 느끼셨다면, 끊임없는 반복 훈련으로 테스팅 고트님께 복종합시다

      🧪 다중 통화를 지원하는 Money 객체

A long time ago...

 

와이캐시라는 채권 포트폴리오 관리 시스템을 만드는 회사가 있었는데...
고객(악마)이 와서 다른 화폐는 왜 안되냐... 사장님(악마)은 할 수 있지? 라고 하고있고
그래서 6개월 동안 (좋은 회사) 뭐 어쩌구 저쩌구 해서 책임 분리하고 해서 완성하니까
회사가치가 몇 곱절 올랐다는 전설같은 이야기

      🧪 다중 통화를 지원하는 Money 객체

TDD 에도 리듬이 있다는데...?

      🧪 다중 통화를 지원하는 Money 객체

형... 그냥 공 차면 안돼요?

      🧪 다중 통화를 지원하는 Money 객체

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

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

3. 코드를 조금 바꾼다

4. 다시 테스트 돌린다

5. 리팩토링

TDD의 리듬 (Cycle)

      🧪 다중 통화를 지원하는 Money 객체

나는 놀라게 된다 (?)

  • 각각 테스트가 기능의 작은 증가분을 어떻게 커버하는지
  • 새 테스트를 돌아가게 하기 위해 얼마나 작고 못생긴 변화가 가능한지
  • 얼마나 자주 테스트를 실행하는지
  • 얼마나 수 없이 작은 단계를 통해 리팩토링이 되어가는지

      🧪 다중 통화를 지원하는 Money 객체

요구사항은 무엇인가 (Before)

      🧪 다중 통화를 지원하는 Money 객체

요구사항은 무엇인가 (After)

      🧪 다중 통화를 지원하는 Money 객체

기능을 완성하려면...

1. 통화가 다른 두 금액을 더해 환율에 맞게 금액을 표시

2. 어떤 금액(주가)을 어떤 수(주식 수)에 곱한 금액을 표시

      🧪 다중 통화를 지원하는 Money 객체

기능을 완성하려면...

1. 통화가 다른 두 금액을 더해 환율에 맞게 금액을 표시 (어려움)

2. 어떤 금액(주가)을 어떤 수(주식 수)에 곱한 금액을 표시

      🧪 다중 통화를 지원하는 Money 객체

코딩부터 노놉 테스트 부터

먼저 이렇게 동작하면 (Operation) 좋겠다 부터 상상하는게 좋다

public void testMultiplication() {
  Dollar five = new Dollar(5);
  five.times(2);
  assertEquals(10, five.amount);
}

      🧪 다중 통화를 지원하는 Money 객체

잠깐, Assertion 작성 방법

JUnit 에서는 (expected, actual) 로 사용합니다

void assertEquals(Object expected, Object actual);

      🧪 다중 통화를 지원하는 Money 객체

이 코드는 무엇이 문제인가

public void testMultiplication() {
  Dollar five = new Dollar(5);
  five.times(2);
  assertEquals(10, five.amount);
}

1. 공용 필드 사용

2. Side Effect

3. Int value

      🧪 다중 통화를 지원하는 Money 객체

문제를 생각해 내는 것도 재능임...

      🧪 다중 통화를 지원하는 Money 객체

이제 Todo를 왼쪽에 적어 둘게요

      🧪 다중 통화를 지원하는 Money 객체

이 코드는 실행이 안된다

public void testMultiplication() {
  Dollar five = new Dollar(5);
  five.times(2);
  assertEquals(10, five.amount);
}
  • Dollar 클래스 없음
  • 생성자 없음
  • times(int amount) 메서드 없음
  • amount 필드 없음

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

      🧪 다중 통화를 지원하는 Money 객체

한 번에 하나씩 (1/4)

class Dollar
  • Dollar 클래스 없음
  • 생성자 없음
  • times(int multiplier) 메서드 없음
  • amount 필드 없음

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

      🧪 다중 통화를 지원하는 Money 객체

한 번에 하나씩 (2/4)

class Dollar {
    Dollar(int amount) {
    
    }
}
  • Dollar 클래스 없음
  • 생성자 없음
  • times(int multiplier) 메서드 없음
  • amount 필드 없음

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

      🧪 다중 통화를 지원하는 Money 객체

한 번에 하나씩 (3/4)

class Dollar {
    Dollar(int amount) {
    
    }
    
    void times(int multiplier) {
    
    }
}
  • Dollar 클래스 없음
  • 생성자 없음
  • times(int multiplier) 메서드 없음
  • amount 필드 없음

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

      🧪 다중 통화를 지원하는 Money 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

한 번에 하나씩 (4/4)

class Dollar {

    int amount;
    
    Dollar(int amount) {
    
    }
    
    void times(int multiplier) {
    
    }
}
  • Dollar 클래스 없음
  • 생성자 없음
  • times(int multiplier) 메서드 없음
  • amount 필드 없음

      🧪 다중 통화를 지원하는 Money 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

테스트가 이제 실패한다

class Dollar {

    int amount = 10;
    
    Dollar(int amount) {
    
    }
    
    void times(int multiplier) {
    
    }
}
  • Dollar.amount 가 10이면 됨

      🧪 다중 통화를 지원하는 Money 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

$5 * 2 = $10

class Dollar {

    int amount;
    
    Dollar(int amount) {
    
    }
    
    void times(int multiplier) {
        amount = 5 * 2
    }
}
  • 작은 단계를 밟아 나가면서 리팩토링을 하자
  • 근데 내가 이렇게 하느냐? 아니...
  • 하지만 연습은 필요합니다

      🧪 다중 통화를 지원하는 Money 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

$5 * 2 = $10

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    void times(int multiplier) {
        amount *= multiplier
    }
}
  • 작은 단계를 밟아 나가면서 리팩토링을 하자
  • 근데 내가 이렇게 하느냐? 아니...
  • 하지만 연습은 필요합니다

      🧪 다중 통화를 지원하는 Money 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

Wrapping up

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    void times(int multiplier) {
        amount *= multiplier
    }
}
  • 테스트 목록 작성
  • 오퍼레이션의 인터페이스를 코드로 표현
  • Todo list 작성
  • 스텁 구현
  • 테스트 통과
  • 리팩토링

      🧪 타락한 객체

프로그래밍의 목적

동작하는 깔끔한 코드를 만드는 것

      🧪 타락한 객체

동작을 시키고 깔끔한 코드를 만들자?

깔끔한 코드를 설계하고 동작을 하도록 하자?

      🧪 타락한 객체

동작을 시키고 깔끔한 코드를 만들자

깔끔한 코드를 설계하고 동작을 하도록 하자?

      🧪 타락한 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

Dollar Side Effect

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    void times(int multiplier) {
        amount *= multiplier
    }
}

지금 코드대로라면 Dollar 객체의 값이 연산하고 나면 바뀐다

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

원하는 형태를 테스트로 작성한다

public void testMultiplication() {
    Dollar five = new Dollar(5);
    Dollar product = five.times(2);
    assertEquals(10, product.amount);
	product = five.times(3);
    assertEquals(15, product.amount);
}
.times() 메소드를 수행하면 새 Dollar 객체가 반환되도록 한다

      🧪 타락한 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

테스트를 통과하는 코드를 작성한다

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier)
    }
}
.times() 메소드를 수행하면 새 Dollar 객체가 반환되도록 한다

      🧪 타락한 객체

최대한 빨리 초록 막대를 보는 방법

 

1. 가짜로(stub) 구현하기: 상수를 반환하게 하거나 원하는 값 (canned answer) 를 반환하게 한다

2. 가짜 구현을 실제로 변경한다

      🧪 타락한 객체

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

테스트를 통과하는 코드를 작성한다

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier)
    }
}
.times() 메소드를 수행하면 새 Dollar 객체가 반환되도록 한다

      🧪 타락한 객체

      🧪 모두를 위한 평등

어떤 정수에 1을 더했을 때 원래 정수가 변하기 보다

1이 더해진 새로운 정수를 갖길 원한다

 

사딸라에 1딸라를 더하면, 새로운 오딸라 객체가 나오길 기대한다

 

이런 것을 값 객체 패턴(Value object parttern) 이라고 합니다

      🧪 모두를 위한 평등

VO Pattern 을 사용하면

기존 값을 변화시키는 걱정은 할 필요가 없습니다

 

다만, 값 객체의 비교를 위해 .equals() 를 구현해야 합니다

또한, 값 객체를 HashMap 의 키로 쓰기 위해 .hashcode() 도 구현

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

테스트를 먼저 만들자

public void testEquality() {
    assertTrue(new Dollar(5).equals(new Dollar(5)));
}
.equals()

      🧪 모두를 위한 평등

 를 구현해, Dollar 객체간 비교를 가능하게 하자

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

일단은 stub 구현으로 통과시키자

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier)
    }
    
    public boolean equals(Object object) {
        return true;
    }
}

assertTrue 를 만족시키기 위해 true 값을 반환한다

      🧪 모두를 위한 평등

      🧪 모두를 위한 평등

삼각측량 전략

 

2개 이상의 테스트를 작성하고, 이 테스트를 바탕으로 추상화를 도출해 낸다

assertEqual(multiply(3, 3), sum(3, 3, 3))

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

삼각측량을 위해 테스트를 추가하자

public void testEquality() {
    assertTrue(new Dollar(5).equals(new Dollar(5)));
    assertFalse(new Dollar(5).equals(new Dollar(6)));
}

아하, 가만 보니 true/false boolean 값을 반환해야 겠구나

      🧪 모두를 위한 평등

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

동치성을 일반화(추상화) 하는 구현을 만든다

class Dollar {

    int amount;
    
    Dollar(int amount) {
        this.amount = amount;
    }
    
    Dollar times(int multiplier) {
        return new Dollar(amount * multiplier)
    }
    
    public boolean equals(Object object) {
        Dollar dollar = (Dollar) object;
        return amount == dollar.amount;
    }
}

비교에 대한 추상화를 통해 구현을 완성했다

      🧪 모두를 위한 평등

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

Equal null

Equal object

하지만, null 또는 다른 객체와의 비교는?

일단은 할 일 목록에 추가해 둔다

      🧪 모두를 위한 평등

🍰 Todo List

 

$5 + 10CHF = $10

$5 * 2 = $10

private amount

Dollar Side Effect

Money 반올림

코드가 안 돌아감

.equals()

.hashcode()

Equal null

Equal object

Wrapping up

  • 우리의 디자인 패턴(값 객체)이 하나의 또 다른 오퍼레이션(equals) 를 암시한다

  • 오퍼레이션을 테스트 하는 코드를 작성했다

  • 오퍼레이션 테스트를 통과시키기 위해 구현을 했다

  • 바로 리팩토링 하지 않고, 삼각측량을 통해 구현을 구체화 했다

  • 구체화 된 구현을 통해 리팩토링을 수행했다

      🧪 모두를 위한 평등

Summary

🎹 리듬에 몸을 맡기고 테스트를 먼저 작성한다

🌼 좋고 아름다운 구현은 나중에 생각한다

🐑 테스트에 복종해라, 이걸 왜 하는지 의심하지 마라

 

혹시 요구사항을 보고 설계부터 떠올렸으면 맴매를 맞자

Made with Slides.com