|
| 1 | +# 단위테스트 |
| 2 | + |
| 3 | +## TDD 법칙 세 가지 |
| 4 | +1. 실패하는 단위 테스트를 작성할 때까지 실제 코드를 작성하지 않는다. |
| 5 | +2. 컴파일은 실패하지 않으면서 실행이 실패하는 정도로만 단위 테스트를 작성한다. |
| 6 | +3. 현재 실패하는 테스트를 통과할 정도로만 실제 코드를 작성한다. |
| 7 | + |
| 8 | +## 깨끗한 테스트 코드 유지하기 |
| 9 | +#### 테스트 코드는 실제 코드 못지 않게 중요하다. 유명무실한, 오히려 방해가 되는 테스트코드를 작성하고 싶지않으면 깨끗한 테스트코드를 유지하자. |
| 10 | + |
| 11 | +### 테스트는 유연성, 유지보수성, 재사용성을 제공한다. |
| 12 | +#### 코드에 유연성, 유지보수성, 재사용성을 제공하는 버팀목은 단위테스트 ! |
| 13 | + |
| 14 | +### 깨끗한 테스트 코드 |
| 15 | +#### 깨끗한 테스트 코드를만들려면? 세 가지가 필요하다. 가독성, 가독성, 가독성 |
| 16 | +ex1) |
| 17 | +``` |
| 18 | +public void testGetPageHieratchyAsXml() throws Exception { |
| 19 | + crawler.addPage(root, pathParser.parse("PageOne")); |
| 20 | + crawler.addPage(root, pathParser.parse("PageOne.ChildOne")); |
| 21 | + crawler.addPage(root, pathParser.parse("PageTwo")); |
| 22 | + |
| 23 | + request.setResource("root"); |
| 24 | + request.addInput("type", "pages"); |
| 25 | + Reponder responder = new SerializedPageResponder(); |
| 26 | + SimpleResponse = response = |
| 27 | + (SimpleResponse) responder.makeResponse ( |
| 28 | + new FitNessContext(root), request); |
| 29 | + String xml = response.getContent(); |
| 30 | + |
| 31 | + assertEquals("text/xml", response.getContentType()); |
| 32 | + assertSubString("<name>PageOne</name>", xml); |
| 33 | + assertSubString("<name>PageOne.ChildOne</name>", xml); |
| 34 | + assertSubString("<name>PageTwo</name>", xml); |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +ex2) |
| 39 | +``` |
| 40 | +public void testGetPageHierarchyAsXml() throws Exception { |
| 41 | + makePages("PageOne", "PageOne.ChildOne", "PageTwo"); |
| 42 | + |
| 43 | + submitRequest("root", "type:pages"); |
| 44 | + |
| 45 | + assertResponseIsXML(); |
| 46 | + assertResponseContains( |
| 47 | + "<name>PageOne</name>", "<name>ChildOne</name>", "<name>PageTwo</name>" |
| 48 | + ); |
| 49 | +
|
| 50 | +} |
| 51 | +``` |
| 52 | +#### 테스트 코드는 본론에 돌입해 진짜 필요한 자료 유형과 함수만을 사용한다. |
| 53 | + |
| 54 | +### 도메인에 특화된 테스트 언어 |
| 55 | +#### 위의 ex2)는 도메인에 특화된 언어, 계속해서 리펙토링을 통해 API들을 생성해나가야한다. |
| 56 | + |
| 57 | +### 이중 표준 |
| 58 | +#### 단순, 간결, 표현력이 풍부해야하지만, 효율적일 필요는 없다. |
| 59 | +ex1) |
| 60 | +``` |
| 61 | +@Test |
| 62 | + public void turnOnLoTempAlarmAtThreashold() throws Exception { |
| 63 | + hw.setTemp(WAY_TOO_COLD); |
| 64 | + controller.tic(); |
| 65 | + assertTrue(hw.heaterState()); |
| 66 | + assertTrue(hw.blowerState()); |
| 67 | + assertFalse(hw.coolerState()); |
| 68 | + assertFalse(hw.hiTempAlarm()); |
| 69 | + assertTrue(hw.loTempAlarm()); |
| 70 | + } |
| 71 | +``` |
| 72 | + |
| 73 | +ex2) |
| 74 | +``` |
| 75 | +@Test |
| 76 | + public void turnOnLoTempAlarmAtThreshold() throws Exception { |
| 77 | + wayTooCold(); |
| 78 | + assertEquals("HBchL", hw.getState()); |
| 79 | + } |
| 80 | +``` |
| 81 | + |
| 82 | +## 테스트당 assert 하나 |
| 83 | +ex1) |
| 84 | +``` |
| 85 | +public void testGetPageHierarchyAsXml() throws Exception { |
| 86 | + makePages("PageOne", "PageOne.ChildOne", "PageTwo"); |
| 87 | + |
| 88 | + submitRequest("root", "type:pages"); |
| 89 | + |
| 90 | + assertResponseIsXML(); //출력이 XML이다. |
| 91 | + assertResponseContains( |
| 92 | + "<name>PageOne</name>", "<name>ChildOne</name>", "<name>PageTwo</name>" |
| 93 | + ); // 특정 문자열을 포함한다. |
| 94 | +} |
| 95 | +``` |
| 96 | + |
| 97 | +ex2) |
| 98 | +``` |
| 99 | +public void testGetPageHierarchyAsXml() throws Exception { |
| 100 | + givenPages("PageOne", "PageOne.ChildOne", "PageTwo"); |
| 101 | + whenRequestIsIssued("root", "type:pages"); |
| 102 | + |
| 103 | + then ResponseShouldBeXML(); |
| 104 | +} |
| 105 | +
|
| 106 | +public void testGetPageHierarchyHasRightTags() throws Exception { |
| 107 | + givenPages("PageOne", "PageOne.ChildOne", "PageTwo"); |
| 108 | + |
| 109 | + whenRequestIsIssued("root", "type:pages"); |
| 110 | + thenResponseShouldContain( |
| 111 | + "<name>PageOne</name>", "<name>ChildOne</name>", "<name>PageTwo</name>" |
| 112 | + ) |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +-> 중복된 코드가 많아짐. |
| 117 | +이 책의 저자는 이 규칙을 무조건적으로 따르지는 않아도 된다고 생각함. 다만, 가능하면 assert는 적게.. |
| 118 | + |
| 119 | +### 테스트 당 개념 하나 |
| 120 | +ex) 메소드를 테스트하는 장황한 코드 |
| 121 | +``` |
| 122 | +public void testAddMonths() { |
| 123 | + SerialDate d1 = SerialDate.createInstance(31, 5, 2004); |
| 124 | + |
| 125 | + SerialDate d2 = SerialDate.addMonths(1, d1); |
| 126 | + assertEquals(30, d2.getDayOfMonth()); |
| 127 | + assertEquals(6, d2.getMonth()); |
| 128 | + assertEquals(2004, d2.getYYYY()); |
| 129 | + |
| 130 | + SerialDate d3 = SerialDate.addMonths(2, d1); |
| 131 | + assertEquals(31, d3.getDayOfMonth()); |
| 132 | + assertEquals(7, d3.getMonth()); |
| 133 | + assertEquals(2004, d3.getYYYY()); |
| 134 | + |
| 135 | + SerialDate d4 = SerialDate.addMonths(1, SerialDate.addMonths(1, d1)); |
| 136 | + assertEquals(30, d4.getDayOfMonth()); |
| 137 | + assertEquals(7, d4.getMonth()); |
| 138 | + assertEquals(2004, d4.getYYYY()); |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +-> 이 테스트코드의 문제는 테스트케이스가 여러개가 있는것이 아니다. 문제는 한 테스트코드에서 여러가지의 개념을 테스트한다는 사실이다. |
| 143 | + |
| 144 | + |
| 145 | +## F.I.R.S.T. |
| 146 | +### 깨끗한 테스트의 다섯가지 규칙 |
| 147 | + |
| 148 | +### 빠르게(Fast) |
| 149 | +#### 테스트는 빨라야한다. 빨라서 자주 돌릴 수 있어야한다. |
| 150 | + |
| 151 | +### 독립적으로(Indepentent) |
| 152 | +#### 테스트는 서로 의존하면 안된다. 만약 테스트가 서로 의존하고 있다면, 한 테스트의 오류를 찾기위해 다른 테스트도 뒤져야한다. 원인 찾기가 너무 힘들어진다. |
| 153 | + |
| 154 | +### 반복가능하게(Repeatable) |
| 155 | +#### 테스트는 어떤 환경에서도 반복 가능해야 한다. 실제 환경, QA 환경, 심지어 네트워크가 안되는 환경에서도 테스트가 가능해야한다. |
| 156 | + |
| 157 | +### 자가검증하는(Self-Validating) |
| 158 | +#### 테스트의 결과는 Bool이다. 로그따위를 써서는 안된다. |
| 159 | + |
| 160 | +### 적시에(Timely) |
| 161 | +#### 테스트는 적시에 작성해야한다. 단위테스트는 테스트하려는 실제 코드를 구현하기 직전에 구현한다. 만일 반대의 순서로 작성을 하게된다면, 테스트코드를 작성하면서 이코드는 테스트코드를 작성하기 힘들구나 라고 생각하는 상황이 올 수 있다. 최악의 경우 테스트가 불가능 할 수 있다. |
| 162 | + |
| 163 | + |
| 164 | + |
| 165 | + |
0 commit comments