단위 테스트(Unit Test), 단위 테스트 도구 적용방법, 단위 테스트는 버그를 찾기 위한 것이 아니다., 하나의 테스트 케이스는 단위 기능 중 하나의 시나리오만 테스트하라. 불필요한 검증 구문을..

CODEDRAGON Development/Software Engineering

반응형

 

 

단위 테스트(Unit Test)

·       소프트웨어 개발에서 단위 테스트인 Unit Testing구현코드의 개별 단위의 적합성 혹은 정확성을 확인 하기 위한 방법입니다. 이 단위의 정의는 테스트 시나리오에 따라 다를 수 있습니다.

·       예를 들어서 C와 같은 절차적 프로그래밍 언어에서는 하나의 단위가 일반적으로 하나의 프로시저 또는 함수입니다. 하지만 객체지향 언어에서는 하나의 메서드가 될 수 있습니다.

·       단위테스트에서 하나의 테스트 단위테스트 가능한 가장 작은 부분으로 생각하면 무난하다.

 

 

 

♣단위 테스트 도구 적용방법

·       단위 테스트는 버그를 찾기 위한 것이 아니다.

·       하나의 테스트 케이스는 단위 기능 중 하나의 시나리오만 테스트하라.

·       불필요한 검증 구문을 작성하지 마라.

·       각 테스트는 독립적이어야 한다.

·       모든 외부 서비스와 상태들에 테스트 더블을 사용해라.

·       시스템 설정파일에 관한 단위 테스트를 작성하지 마라.

·       단위 테스트 케이스의 이름은 명확하고 일관되게 테스트의 의미를 반영해야 한다.

·       외부 시스템이나 서비스에 대한 의존성이 가장 낮은 메서드들에 대해 테스트를 먼저 작성하라. 그리고 확장해 가라.

·       예상된 예외 사항을 테스트하는 단위 테스트 코드를 작성하라.

·       가장 적합한 검증 구문을 사용하라.

·       검증 구문 파라미터들은 적합한 순서대로 배치하라.

·       테스트를 위한 코드는 제품 코드에서 분리되어야 한다.

·       정적 변수를 테스트 클래스에 사용하지 마라. 만약 사용했다면 각 테스트 케이스 실행시마다 재초기화해라.

·       예외 발생 시 단순히 테스트를 실패하기 위한 Catch 구문을 작성하지 마라.

·       간접적인 테스트들에 의존하지 마라.

·       단위 테스트를 자동으로 실행하게 빌드 스크립트를 작성해라.

·       단위 테스트들의 실행을 생략하지 마라.

·       테스트 결과를 XML 형태로 출력하라.

 

 

 

단위 테스트는 버그를 찾기 위한 것이 아니다.

·       단위 테스트의 의도를 정확히 이해하는 것은 중요합니다. 단위 테스트는 단순히 버그를 찾기 위한 효과적인 방법이 아니다. 정의에 따르면 단위 테스트는 시스템의 각각의 단위들을 개별적으로 조사하는 것이다. 시스템이 구현되어 실제 환경에서 동작할 때 모든 단위들은 완벽하게 하나의 유기체로 동작해야 합니다. 하지만 시스템은 각 독립적 으로 테스트되는 단위의 단순한 결합 그 이상으로 복잡하고 또한 에러가 발생하기 쉽습니다. 컴포넌트 X, Y가 독립적으로 잘 작동한다는 것이 이 컴포넌트들이 서로 호환된다든가 혹은 정확하게 조합되어졌다는 것은 아닙니다.

·       따라서 단순히 버그를 찾기 위한 것이라면 일반적으로 검증자가 일일이 테스트하듯 이 전체 시스템을 실제 통합환경에서 실행하는 것이 훨씬 효과적인 테스트가 될 수 있습니다. 그리고 이러한 테스트를 자동화한 것을 통합 테스트라고 하는데, 이것은 일반적으로 단위 테스트와는 다른 기술들을 사용합니다.

·       "단위 테스트는 TDD(Test Driven Development)에서 그러한 것처럼 반드시 시스템 디자인 단계의 일부분으로 보아야 합니다." 이렇게 함으로써 시스템 디자이너는 시스템의 가장 작은 단위 모듈을 인식할 수 있고 또한 개별적으로 테스트할 수 있습니다.

 

 

 

하나의 테스트 케이스는 단위 기능 중 하나의 시나리오만 테스트하라.

 

·       단위 테스트 작성 시 가장 중요하게 인식할 점은 테스트 단위가 복수의 테스트 시나리오들을 가질 수 있다는 것이다. 그리고 모든 테스트 시나리오들은 독립적인 테스트코드로 작성되어져야 한다.

·       예를 들어 두 매개변수를 가지고 처리한 후 값을 돌려주는 함수의 테스트 케이스를 작성한다고 하면, 다음과 같은 테스트 시나리오가 가능할 것이다.

o   첫 번째 파라미터가 널 값일 경우 예외 객체를 반환해야 한다.

o   두 번째 파라미터가 널 값일 경우 예외 객체를 반환해야 한다.

o   두 개의 파라미터 모두가 널 값일 경우 예외 객체를 반환해야 한다.

o   파라미터가 정상 범위 안일 경우 작업 실행 후 결과 값을 반환해야 한다.

·       이러한 세분화된 테스트 케이스들은 코드를 수정하거나 리택토링 시 효과적이다. 왜냐하면 단위 테스트만 수행하면 코드의 수정이 코드의 의도된 기능을 망가뜨렸는지 확인할 수 있기 때문이다. 또한 기능을 수정한다면 최소한의 테스트 코드만 수정하면되기 때문이다.

 

 

 

불필요한 검증 구문을 작성하지 마라.

·       단위 테스트는 시스템의 특정 단위가 어떻게 동작하는지에 대한 디자인 스펙이지 단순히 단위 내의 코드가 행하는 모든 것을 관찰하는 것이 아니다.

·       단위 내의 모든 것에 대해 검증구문을 작성하지 마라. 대신 테스트하려는 하나의 시나리오에 집중한다. 테스트 코드를 이렇게 작성하지 않으면 하나의 이유로 여러 테스트 케이스가 실패 할 수 있다. 결국 이것은 프로젝트에 아무런 도움이 되지 않는다.(왜냐하면 테스트 코드를 보고 문제점을 찾을 수 없기 때문이다.)

 

 

 

 

각 테스트는 독립적이어야 한다.

·       다른 테스트에 의존적인 꼬리에 꼬리를 무는 단위 테스트케이스를 작성하지 않는다. 이러한 테스트들은 테스트의 근본적인 실패 원인을 테스트 결과를 통해 알 수 없다.

·       결국 별도의 디버깅 작업을 수행해야 한다. 또한 이러한 상호 의존적인 테스트 코드는 유지보수도 번거롭다. 왜냐하면 하나의 테스트 코드를 수정할 경우 의존성을 가지고 있는 다른 코드도 수정해야 할 경우가 생기기 때문이다.

·       테스트의 선결 조건을 설정하기 위해서는 @Before/@After와 같은 테스트 프레임워크가 제공하는 어노테이션을 사용해라.        만약 서로 다른 테스트 구문을 위해 @Before/@After 어노테이션 구문 안에서 여러 가지 다른 세팅을 해야 한다면 별도의 새로운 테스트 클래스를 생성하는 것을 고려한다.

 

 

 

모든 외부 서비스와 상태들에 테스트 더블을 사용해라.

·       그렇게 하지 않으면 공통된 외부 조건를 사용하는 테스트 구문들의 결과가 서로에게 영향을 미친다. 결국 테스트 구문 실행 순서에 따라 테스트 결과가 달라지거나 네트워크 망이나 데이터베이스의 조건에 따라 결과가 달라진다.

·       게다가 외부 서비스의 버그들로 인해서 테스트 결과가 실패로 끝날 수 있다(테스트구문이 정적 변수들을 변화시키게 하지 않는다. 어쩔 수 없이 변화시켜야 한다면 적어도 테스트 시작 바로 전에 이 변수들을 초기화 시켜야 한다.).

 

 

시스템 설정파일에 관한 단위 테스트를 작성하지 마라.

·       정의에 따르면 시스템 설정은 단위 테스트의 범위가 아니다(그래서 그러한 별도의 설정파일로 분리된다). 시스템 설정값에 대한 단위 테스트를 작성하고 싶다면 설정값을 읽는 모듈에 대한 하나 혹은 두 개의 경우만 테스트한다.

·       시스템 설정에 대한 모든 경우의 수를 테스트를 하는 것은 결국 하나밖에 증명하지 못한다. "난 복사 후 붙여넣기 할 줄 알아요"

 

 

 

단위 테스트 케이스의 이름은 명확하고 일관되게 테스트의 의미를 반영해야 한다.

·       이것은 언제나 명심하고 실천해야 하는 중요한 점이다. 테스트 케이스의 이름은 항상 테스트의 의도가 무엇인지 반영해야 한다. 단순하게 테스트하려는 하는 단위의 클래스와 메서드의 조합을 테스트 케이스의 이름으로 사용하는 것은 좋은 생각이 아니다.

·       클래스나 메서드의 이름을 변경해야 하는 경우가 생길 때 테스트 케이스들의 이름도 매번 수정해야 한다.

·       그러나 단위 테스트 케이스의 이름이 단위의 기능을 반영하는 논리적인 이름이라면 단위 기능이 바뀌지 않는 경우에는 테스트 케이스 이름은 언제나 동일하다.

 

아래는 테스트 케이스 이름의 좋은 예들이다.

TestCreateEmployee_NullId_ShouldThrowException

TestCreateEmployee_NegativeId_ShouldThrowException

TestCreateEmployee_DuplicateId_ShouldThrowException

TestCreateEmployee_ValidId_ShouldPass

 

 

 

외부 시스템이나 서비스에 대한 의존성이 가장 낮은 메서드들에 대해 테스트를 먼저 작성하라. 그리고 확장해 가라.

·       예를 들어, Employee 모듈을 테스트한다고 하자. 가장 먼저 Employee 모듈을 생성하는 코드부터 테스트를 작성한다. 왜냐하면 이 시나리오가 가장 낮은 외부 의존성을 가지기 때문이다. 이 시나리오가 성공한다면, 데이터베이스에 접근하는 테스트 코드를 추가한다.

·       데이터베이스에 Employee 정보를 가질려면 먼저 Employee 모듈을 생성하는 테스트 시나리오를 통과해야 한다. 만약 모듈을 생성하는 코드에 버그가 있다면 훨씬 빨리 발견할 수 있다.

 

 

 

예상된 예외 사항을 테스트하는 단위 테스트 코드를 작성하라.

예상된 예외 사항을 검증하기 위한 테스트 케이스를 작성해야 하는 경우도 있다. 이때에는 아래와 같이 try/catch 구문으로 결과를 검증하려고 하지 않는다.

try{

methodOccursDomainSpecificException()

assertFail("Exception should be occurred");

}catch(DomainSpecificException e){

 

}

 

대신 아래와 같이 JUNIT이 제공하는 아래의 방법을 사용해라.

@Test(expected=SomeDomainSpecificException.SubException.class)

 

 

 

가장 적합한 검증 구문을 사용하라.

각 테스트 케이스에 사용할 수 있는 많은 검증 구문이 있을 수 있다. 하지만 각 경우마다 이유와 의도에 맞게 가장 적합한 것을 사용한다.

 

 

 

검증 구문 파라미터들은 적합한 순서대로 배치하라.

검증 구문은 대개 두 개의 파라미터를 취한다. 첫 번째 것은 테스트 결과가 패스할 때 기대하는 정상 값이고, 두 번째 것은 테스트 결과 값이다. 이 두 값들을 순서대로 작성하라. 이렇게 하면 테스트 실패 시 에러 메세지를 통해 무엇이 잘못되었는지 쉽게 확인할 수 있다.

 

 

 

테스트를 위한 코드는 제품 코드에서 분리되어야 한다.

·       빌드 스크립트에서 테스트를 위한 코드는 실제 제품 코드와 같이 전달되지 않게 한다.

·       테스트 코드 내에서 아무것도 출력하지 않는다(Do not print anything out in unit tests).

·       만약 테스트 케이스가 가이드라인들에 따라 제대로 작성되어졌다면 별도의 출력문이 필요하지 않다. 만약 출력문에 대한 필요성을 느낀다면 테스트 코드를 재검증 하라. 무엇인가 테스트 코드 중 잘못된 점이 있다.

 

 

 

정적 변수를 테스트 클래스에 사용하지 마라. 만약 사용했다면 각 테스트 케이스 실행시마다 재초기화해라.

이미 각 테스트들 간의 독립성에 대해 중요성을 언급했다. 따라서 절대 Static 변수들을 사용하지 않는다. 만약 어쩔수 없이 사용해야 하는 경우가 발생한다면 각 테스트 케이스의 시작 전에 반드시 재초기화시킨다.

 

 

 

예외 발생 시 단순히 테스트를 실패하기 위한 Catch 구문을 작성하지 마라.

테스트 코드 내에 어떤 메서드가 예외를 발생시킬 수 있다면 단순히 테스트를 실패하기위해 Catch 구문을 사용하지 않는다. 대신 Throws 구문을 테스트 코드 선언 시에 사용한다. 되도록이면 별도의 특정 예외 객체보다는 일반적이 Exception 예외 객체를 사용한다. 이 가이드라인은 테스트 커버러지 증가에도 도움이 된다.

 

 

 

간접적인 테스트들에 의존하지 마라.

하나의 테스트가 본래 의도된 시나리오 외 또 다른 시나리오도 테스트 한다고 가정하지않는다. 이것은 혼란만 가져온다. 대신 또 다른 시나리오에 대한 추가적인 테스트 케이스를 작성한다.

 

 

 

단위 테스트를 자동으로 실행하게 빌드 스크립트를 작성해라.

테스트 케이스들이 빌드 스크립트를 통해 자동적으로 실행되게 한다. 이것은 테스트 실행환경과 애플리케이션의 신뢰성을 높여준다.

 

 

 

단위 테스트들의 실행을 생략하지 마라.

만약 단위 테스트의 코드가 적절하지 않다면 코드를 삭제하고, @Ignore 어노테이션을 사용하지 않는다. 소스코드의 부적절한 테스트 케이스를 포함하는 것은 아무런 도움이 되지 않는다.

 

 

 

테스트 결과를 XML 형태로 출력하라.

즐거운 것은 좋은 것이다. 이 가이드라인은 테스트 케이스 작성을 즐겁게 한다. 예를 들어 JUNIT을 빌드 스크립트와 통합시켜서 XML­로 테스트 결과를 출력 후 이것을 ­추가적인 포맷팅 작업을 통해 테스트 결과를 보기 좋게 바꿀 수 있다.