Undergoing

Controller Component 작성 - 3 본문

개발/TDD

Controller Component 작성 - 3

Halkrine 2012. 6. 11. 17:08

예외 처리 테스트는 매우 중요한 기법이다. 의도치 않은 상황까지 고려하여 미연에 방지하고 프로그래밍해야 하기 때문이다. 하지만 임의로 오류 상황을 만드는 데에는 많은 시간이 소요된다. 또한 수동으로 만들어내지 못하는 오류도 얼마든지 있다. 따라서 요구사항에 예외 처리를 포함하는 것은 매우 중요하다.


다음은 예외 상황을 만들어주는 요청 핸들러이다.


private class SampleExceptionHandler implements RequestHandler

{

public Response process(Request request) throws Exception

{

throw new Exception("프로세싱 요청 중 에러 발생");

}


이를 포함하여 테스트 메서드를 만들어 핸들러를 등록하고 요청을 처리해보자.


@Test

public void testProcessRequestAnswersErrorResponse()

{

SampleRequest request = new SampleRequest();

SampleExceptionHandler handler = new SampleExceptionHandler();

controller.addHandler(request, handler);

Response response = controller.processRequest(request);

assertNotNull("null 응답을 보내면 안 됩니다", response);

assertEquals(ErrorResponse.class, response.getClass()); 


이를 그대로 돌리면 실패가 발생하는데, 두 가지를 알 수 있다.

- 이미 [테스트] 라는 이름의 요청이 등록되어 있기 때문에 다른 이름을 찾아봐야 한다.

- 예외 처리 코드를 더 추가하여 생산 제품에서도 RuntimeException이 발생하는 것을 막아야 한다.


SampleRequest가 매번 다른 이름으로 생성될 수 있도록 수정해주는 것이 현명한 선택이다. 


...

public class SampleRequest implements Request

{

//요청의 이름을 담아두기 위한 필드를 하나 만들고, 이전 코드에서 사용한 기본 값을 할당

private static final String DEFAULT_NAME = "테스트";

private String name;

//기본 생성자를 오버라이드하기 위해 요청의 이름을 받아들일 수 있는 생성자를 추가

public SampleRequest(String name)

{

this.name = name;

}

public SampleRequest()

{

this(DEFAULT_NAME);

}

public String getName()

{

return this.name;

}

...

@Test

public void testProcessRequestAnswersErrorResponse()

{

SampleRequest request = new SampleRequest("testerror"); //새로 소개한 생성자를 호출하도록 수정

SampleExceptionHandler handler = new SampleExceptionHandler();

controller.addHandler(request, handler);

Response response = controller.processRequest(request);

assertNotNull("null 응답을 보내면 안 됩니다", response);

assertEquals(ErrorResponse.class, response.getClass());

}

...


위 코드들은 예외 상황을 시뮬레이션하기 위해 작성된 것이고, 이제는 예외를 테스트하기 위한 코드를 작성해본다. 테스트 도중, 중복된 이름의 요청을 등록하려 시도하면 addHandler 메서드가 문서화되지 않은 RuntimeException을 발생시킬 것이다. 코드에 의하면, 등록된 요청이 없을 때 getHandler를 호출하면 RuntimeException을 발생시키도록 구현했음을 알 수 있다. 메서드의 동작이 현재의 설계에 부합하는지 검사하는 테스트를 몇 개 작성해보자.


 ...

@Test(expected=RuntimeException.class)

public void testGetHandlerNotDefined()

{

SampleRequest request = new SampleRequest("testNotDefined");

controller.getHandler(request);

}

@Test(expected=RuntimeException.class)

public void testAddRequestDuplicateName()

{

SampleRequest request = new SampleRequest();

SampleHandler handler = new SampleHandler();

controller.addHandler(request, handler);

}

...


상기 테스트 메서드는 addHandler와 getHandler가 제때에 Runtime Exception을 던지는지 검사한다. 

- @Test(expected=RuntimeException.class)는 기대하는 예외사항이 무엇인지를 기술한다. 이 구문에서는 RuntimeException 클래스가 작동되기를 기대한다. 이 테스트는 예외 사항을 위한 것이므로 testGetHandler를 접두로 쓰고 뒤에 NotDefined를 추가하였다.

- controller.getHandler(request); : 전달된 요청을 처리할 핸들러가 지정되지 않았으므로 RuntimeException이 발생해야 한다.


이제는 주어진 시간 내에 동작을 완료할 수 있는지 검사하는 테스트를 작성해보려 한다. JUnit은 주어진 시간 내에 완료하지 못하면 테스트는 실패라 판단한다. 이를 위해 timeout이라는 파라미터를 활용한다.


@Test(timeout=70)

public void testProcessMultipleRequestTimeOut()

{

Request request;

Response response = new SampleResponse();

RequestHandler handler = new SampleHandler();

for(int i = 0 ; i < 99999 ; i++)

{

request = new SampleRequest(String.valueOf(i));

controller.addHandler(request, handler);

response = controller.processRequest(request);

assertNotNull(response);

assertNotSame(ErrorResponse.class, response.getClass());

}


for 순환문을 사용, 99999개의 SampleRequest 객체를 생성하여 핸들러와 함께 컨트롤러에 추가한다. 그리고 컨트롤러의 processRequest()메서드를 호출하고, 반환된 객체가 null이나 ErrorResponse가 아님을 단언한다.


여태까지 작성했던 포스팅에 있는 소스를 사용할 경우, timeout을 75밀리초 미만으로 하면 에러가 자주 발생한다. testProcessMultipleRequestTimeOut() 메서드에서 시간이 오버되는데, timeout을 바꿀 때마다 소요시간이 바뀐다. 70밀리초가 걸릴 경우도 있지만, 평균적으로 75밀리초 안팎인 것 같다.


* 실행 시간은 구동 하드웨어(프로세서 속도, 가용 메모리양 등)와 소프트웨어(운영체제와 자바의 버전 등)에 따라 달라짐.


가끔 타임아웃 테스트가 전체 빌드를 말아먹는 경우가 있다. 그래서 일부 테스트들은 skip해야 좋을 때도 있는데, 이를 가능하게 하려면 @Test 메서드에 @Ignore 애노테이션을 선언하면 끝난다(JUnit 4.x Only)


@Test(timeout=70)

@Ignore(value = "이상적인 시간 제한을 정하기 전까지 이 기능을 무시합니다")

public void testProcessMultipleRequestTimeOut()

{

...


테스트를 건너뛰는 이유는 value 파라미터를 이용해 기입한다.

 

* JUnit 3.x에서는 테스트 메서드의 이름을 바꾸거나 주석 처리를 함으로써 테스트를 건너뛰었다.



'개발 > TDD' 카테고리의 다른 글

Test Code 작성 규율  (0) 2012.06.27
Controller Component 작성 - 2  (0) 2012.06.11
Controller Component 작성 - 1  (0) 2012.06.11
JUnit의 개요  (0) 2012.06.07