---
# System prepended metadata

title: 'TDD강의, 테스트 주도 개발 기초 Chapter #1, #2'
tags: [강의, 냥하]

---

# TDD강의, 테스트 주도 개발 기초 Chapter #1, #2

###### tags: `냥하`, `강의`

## Intro
### 과학 vs 엔지니어링
과학: 세상의 많은 현상에 대한 왜?를 밝혀내는 것
엔지니어링: 문제를 정의함
```
=> 엔지니어링에게 과학은 목적이 아닌 수단
```

### 패턴
- 알려진 문제의 일반적이고 재사용할 수 있는 해결법
- 프로그래머는 고유한 문제를 풀어야 한다.
- 고유한 문제는 좀 더 작은 하위 기술 문제(ex, 이미 누군가가 풀어서 풀이법이 있는 문제)를 가진다.
- 이 작은 기술 문제들 중 많은 일부는 과거 어디에선가 여러번 반복되고 해결된 것이 있다.

```
Patterns always have two pars: the how and the when

해법보다 문제에 집중하라. 만병통치약은 없다. 은탄환(silver bullet)은 없다
다양한 무기를 준비해서 잘 꺼내서 쓸 수 있어야한다.
```
 
 ## 1부:: 테스트 주도 개발 기초
 ### 1. 코드 기능 명세
: 프로그래머에게 올바른 명세를 줄 필요가 있다.
<br/><br/>

 ### 2. 테스트 기법
- 수동테스트
- 소프트웨어 회귀 : 원래는 동작했는데, 어떤 시점 이후로 동작하지 않을 때
    - 대상: 시간이 갈수록 회귀 테스트의 대상이 늘어남, 코드는 변경된 영역 외 기존 영역에도 영향을 미칠 수 있기 때문.
- 테스트 자동화
    - 기능을 검증하는 코드 작성
    - 테스트 코드 작성 비용이 소비되지만, 실행 비용이 낮고 결과의 신뢰도가 높음
    - 테스트 코드 작성과 관리가 프로그래머 역량의 영향을 크게 받음.
- 인수 테스트
    - 만들어진 소프트웨어를 사용자에게 인수하면서 검증하는 과정, 사용자의 환경과 가장 비슷한 환경(시스템 전체 내용 Deploy후)에서 진행
    - 배치가 완료된 시스템을 대상으로 검증
    - 신뢰도가 높음
    - 높은 비용(작성비용, 관리비용, 실행비용)
    - 피드백 품질이 낮음
- 단위 테스트
    - 시스템 일부(하위 시스템)을 대상으로 검증
    - 낮은 비용
    - 높은 피드백 품질
    - 전체 시스템 이상 여부 신뢰도가 낮음
<br/><br/>

### 3. 코드 분해 
:문제를 나눌 수 있다. 큰 시스템은 더 작은 하위 시스템으로 분해 가능, 교체 가능

- 반복되는 문제의 풀이는 재사용이 가능하다
    - 코드는 여러번 읽히기 때문에, 코드 가독성이 중요하다
- 소프트웨어 개발비용 절감 ( 엔지니어링 철학 관점 )

#### 모듈화
- 모듈 재사용,라이브러리
- 인터페이스(모듈이 어떤 기능을 나타내는 지에 대한 설계) - 구현
<br/><br/>

### 4. 단위 테스트
`npm install jest`
package.json -> npm test를 `jest --watch` 로 변경
```
// 공백 압축
function refineText(s) {
    return s.replace("    ", "  ").replace("  ", " ")
}
```

```
// index.test.js
const sut = require("./index") // 테스트 대상 시스템 : system under test

test('sut transform "hello  world" to "hello world"'),() => { 
    // 사이 공백 2개 > 1개가 되는지?
    const actual = sut("hello. world");
    expect(actual).toBe("hello world");
});
```

이러한 테스트 코드를 여러개 짜고싶다고 생각했을때,
여러번 쓰는 것이 아닌 반복문 처리
```
test('sut correctly works', () => {
    for(const source of ['hello  world', 'hello   world', 'hello    world']) {
        // 공백이 각각 2개, 3개, 4개인 것에 대한 테스트
        const actual = sut(source);
        expect(actual).toBe("hello world")
    }
})

```
위 테스트 코드의 문제점
- 어떤 테스트 케이스에서 실패 했을지를 알 수 없음
- 중간에 실패하면 뒤에까지 실행이 안됨 -> 코드는 줄어들었지만, 피드백 품질이 낮아짐

해결 방법
- parameter test `describe.each`: 동일 코드에서 인자만 달리해서 테스트를 할 수 있는 기법

```
test.each`
    source | expected
    ${"hello  world"} | ${"hello world"} 
    ${"hello   world"} | ${"hello world"} 
    ${"hello   world"} | ${"hello world"} 
`('sut transforms "$source" to "$expected"', {(source, expected)}) => {
        const actual = sut(source);
        expect(actual).toBe(expected)
}
```
![image](https://user-images.githubusercontent.com/48500209/163295338-cacc1cff-1173-4411-a45a-7b5b5de62acf.png)

=> 어떤 테스트 케이스에서 실패했는지를 알 수가 있음
테스트 코드는 줄이면서 품질은 그대로 가져가는 케이스
<br/><br/>

### 5. 테스트 우선 개발
: 운영 코드보다 테스트 코드를 먼저 작성, 코드 기능명세와 매우 유사
가시적이고 구체적인 목표 | 자가 검증 | 반복 실행 | 클라이언트
- 운영 코드보다 테스트 코드를 먼저 작성
    - 명확하고 검증 가능한 목표를 설정한 후 목표를 달성
    - 프로세스가 코딩에 앞선 목표 설정을 강요
    - 프로그래머는 자신이 풀어야 할 문제를 구체적으로 이해해야함


강의 플로우: 계속 test code를 작성하고, 이를 통과하는 운영코드를 작성하는 방법

😿 느낀점 => 뭔가 주니어단계에서 tdd를 하다보면 볼품없는 하드코딩하는 운영코드를 작성하게될까바 더 안하게 됐던듯


`npm i faker` => 임의의 문자열을 넣어서 테스트
```javascript=
describe("given banned word", () => {
    const bannedWord = faker.lorem.word()
    const source = "hello " + bannedWord;
    const expeceted = "hello " + "*".repeat(bannedWord.length);

    test(`${bannedword} when invoke sut then it returns ${expected}`, () => {
        const actual = sut(source, {bannedWords: [bannedWord]});
        expect(actual).toBe(expected);
    })
})

```


운영코드
```javascript=
function refineText(s, options) { // 금지어를 사용하는 case에서만 options 파라미터를 넣고싶음
    s.replace("    ", "  ").replace("  ", " ")
    
    if(options) {
        for(const bannedWord in option.bannedWords) {
            s = s.replace(bannedWord, "*".repeat(bannedWord.length));
        }
    }
    return s;
}
```
<br/><br/>


### 6. 정리된 코드(리팩토링)
- 작업환경 정리
- 리팩터링
    - cf, 팩터링: 숫자나 식을 여러 요소로 나누는 것, 요소의 크기는 단순하게
    - 의미를 유지하며 코드 베이스를 정리하는 것
    - 전체 시스템의 의미를 유지하며, 하위 시스템의 내부 구조를 개선하는 것을 의미함
        - 의미 유지를 확인하는 방법: 테스트
    - 강의 실습: 메서드를 추출 / if문을 switch문으로 변경(자바 화살표 모야...?) / 쓰지않는 parameter제거 / 가독성 up 등등. 
        - => 이렇게 코드를 바꾸면서 중간중간 테스트코드를 계속 실행시켜봄(의미 유지)

    ```javascript=
    // 테스트는 바꾸지 않고, 운영코드를 개선하는 것이 원칙
    
        function refineText(source, options) { 
            // 금지어를 사용하는 case에서만 options 파라미터를 넣고싶음
            [normalizeWhiteSpaces, compactWhiteSpaces, maskBannedWords].reduce((value, filter) => filter(value, options), source)
            // source = normalizeWhiteSpaces(source);
            // source = compactWhiteSpaces(source);
            // source = maskBannedWords(source, options);
            
            return source;
        }
        
        function normalizeWhiteSpaces(source) {
            return source.replace("\t", " ");
        }
        
        // 연쇄적으로 replace하던 긴 코드를 추출함
        function compactWhiteSpaces(source) {
            return source.indexOf("  ") < 0 ? source : compactWhiteSpaces(source.replace("  ", " "));
        }
        
        function maskBannedWords(source, options) {
            if(options) {
                for(const bannedWord in option.bannedWords) {
                    source = source.replace(bannedWord, "*".repeat(bannedWord.length));
                }
            }
            return source;
        }
    ```
<br/><br/>
    
    
    
### 7. 테스트 주도 개발
- 절차: RED -> GREEN -> REFACTOR
    - RED: 테스트의 실패를 확인하는 단계
    - GREEN: 운영 코드를 수정해서 테스트를 모두 성공시키는 과정
    - REFACTOR: 테스트 통과를 유지하며 리팩터링 진행
- 테스트 실패: 테스트코드가 실패하는지 확인하는것이 중요
- 테스트 성공: 테스트 성공은 요구사항 만족을 의미
- 켄트 백의 설계 규칙
    1순위: 테스트 통과
    2순위: 코드의 의도드러내기, 가독성, 중복 코드 제거(but 의도와 상충할 수 있음)
    
```javascript=
trimWhitespaces

function trimWhitespaces(value) {
    return value.trim()
}
```

<br/><br/>

### 8. 프로그래머 피드백
- 기대 출력 피드백
    1. 사용자 피드백: 사용자가 직접코드를 사용한 후 경험한 버그나 불만을 제보
    2. Quality Assurance(QA): 전문 인적 자원에 의한 인수 테스트
    3. 프로그래머 테스트: 프로그래머가 직접 피드백 장치를 준비
        - 코드 작성할때는 비용 up, 실행 비용은 down
        - 문제 조기발견 가능
    4. 도구 피드백: 컴파일 오류, 정적 검사 등 프로그래머가 사용하는 도구가 제공하는 피드백
- 오버엔지니어링
    - 프로그래머는 요구사항 명세에 명확히 지정되지 않은 성능 달성이나 구현 설계 품질 개선에 빠져드는 경향을 가짐
    - 모든 테스트에서 GREEN을 확인하면 넘어갈줄도 알아야함

=> 핵심은 피드백
- 테스트 주도 개발의 핵심은 정해진 절차가 아니라 짧은 주기로 지속되는 피드백
- 피드백에 기반해 안정적으로 지식과 코드를 늘려나가는 것이 목적.

### 9. 실습(장난감)
1부터 100까지 임의의 정수 맞추기 게임
자바코드라 이해에 중심을 두고 안적음.