테스트케이스가 있다면 개발자는 변경을 두려워 하지 않는다. 잠재적 버그에 대해 안심하고 개선할 수 있다.
깨긋한 테스트 코드에는 세 가지가 필요하다.
public void testGetPageHieratchyAsXml() throws Exception{
crawler.addPage(root, PathParser.parse("PageOne"));
crawler.addPage(root, PathParser.parse("PageOne.ChildOne"));
crawler.addPage(root, PathParser.parse("PageTwo"));
request.setResource("root");
request.addInput("type","pages");
Responde responder = new SerializedPageResponder();
SimpleResponse response = (SimpleResponse) responder.makeResponse(new FitNesseContext(root), request);
String xml = response.getContent();
assertEquals("text/xml", response.getContentType());
assertSubString("<name>PageOne</name>",xml);
assertSubString("<name>PageTwo</name>",xml);
assertSubString("<name>PageChildOne</name>",xml);
}
솔직히 뭐하는 코드인지 잘 모르겠고 중복코드가 있긴한데 어차피 다 케이스를 생성해야 하는게 아닌가 하는 생각이 들었다.
public void testGetPageHieratchyAsXml() throws Exception{
makePages("PageOne","PageOne.ChlidOne","PageTwo");
submitRequest("root","type:pages");
assertResponseIsXml();
assertResponseContains("<name>PageOne</name>","<name>PageTwo</name>","<name>PageChildOne</name>");
}
확실히 메소드로 빼니까 가독성이 훨씬 좋아지고 테스트코드가 어떤 작업을 하려는지 훨씬 잘 이해된다.
BUILD-OPERATE-CHECK 패턴이 위와 같은 테스트 구조에 적합하다.
1. 첫 부분은 테스트 자료를 만든다.
2. 두 번째 부분은 테스트 자료를 조작한다.
3. 세 번째 부분은 조작한 결과가 올바른지 확인한다.
시스템 조작 API를 사용하는 대신 API위에 함수와 유틸리티를 구현하여 테스트 코드를 짜기도 읽기도 쉽게한다.
테스트API 코드에 적용하는 표준은 실제 코드에 적용하는 표준과 다르다.
단순하고, 간결하고, 표현력이 풍부해야 하지만 실제 코드만큼 효율적일 필요는 없다.
@Test
public void turnOnLoTempAlarmAtThreashold() throws Exception{
hw.setTemp(WAY_TOO_COLD);
controller.tic();
assertTrue(hw.heaterState());
assertTrue(hw.blowerState());
assertTrue(hw.coolerState());
assertTrue(hw.hiTempAlarm());
assertTrue(hw.loTempAlarm());
}
tic함수는 뭘할까? heaterState를 보고 assertTrue를 보며 각 메소드와 assert테스트 메서드를 다 확인해야한다.
@Test
public void turnOnLoTempAlarmAtThreashold() throws Exception{
wayTooCold();
assertEquals("HBchl",hw.getState());
}
HBchl은 위 Heater부터 loTemp까지의 앞글자를 가져온 이상한 문자열이다. 대문자는 켜짐을 뜻하고 소문자는 꺼짐을 뜻한다. 이 방식이 실제코드에서는 좋지 않은 코드이지만 테스트 코드에서는 적절하다. 읽기도 쉽고 이해하기도 훨씬 쉽다.
public String getState(){
String state = "";
state += heater ? "H" : "h";
state += blower ? "B" : "b";
state += cooler ? "C" : "c";
state += hiTempAlarm ? "H" : "h";
state += loTempAlarm ? "L" : "l";
}
StringBuffer는 성능이 좋지만 가독성이 떨어진다. 그래서 비용이 크게 차이가 나지 않으면 사용하지 않는다. 하지만 임베디드 환경은 제한적인 자원과 메모리로 인하여 StringBuffer를 사용해야 한다. 하지만 이건 테스트 코드다. 자원이 제한적일 가능성이 낮기 때문에 테스트 환경에서는 전혀 문제가 없다.
assert문이 하나라면 함수는 결론이 하나다. 물론 하나의 assert를 만들기 위해 배보다 배꼽이 커지는 상황이 온다면 assert문을 여럿 사용하는 편이 좋다. 하지만 최대한 줄이는게 좋다.
assert문이 여러개인 것 보다 한 테스트에서 여러 개념을 테스트하는 것이 문제이다.