테스터가 말하는 테스트코드 작성 팁과 사례

28
(Junit) 테스트케이스 작 팁 2011.01 by JungGun home: genycho.blog.me ※ 상업적 용 및 출처를 밝히지 않 무단 사용 금합니다

Upload: sangin-choung

Post on 07-Jan-2017

337 views

Category:

Software


1 download

TRANSCRIPT

사례기반 (Junit) 테스트케이스작성 팁

2011.01 by JungGunhome: genycho.blog.me

※ 상업적 이용 및 출처를 밝히지 않은 무단 사용을 금합니다

What…테스트를 하는 사람 입장에서

좋은 테스트와 테스트코드를 말하기 위함

현장에서 경험했던 사례를 예로 들어,

특정 개발언어, 단위테스트에만 적용되는게 아니라

모든 테스트 코드에 적용될 수 있는 내용

좋은 테스트와 테스트 코드?

당신의당신의당신의당신의 테스트테스트테스트테스트 코드는코드는코드는코드는 안녕하십니까안녕하십니까안녕하십니까안녕하십니까????

작성한작성한작성한작성한 테스트가테스트가테스트가테스트가

모두모두모두모두 성공하는데성공하는데성공하는데성공하는데

그럼그럼그럼그럼 테스트를테스트를테스트를테스트를 잘잘잘잘 한한한한 걸까걸까걸까걸까????

테스트테스트테스트테스트 커버리지커버리지커버리지커버리지가가가가

80% 80% 80% 80% 넘게넘게넘게넘게 나왔는데나왔는데나왔는데나왔는데

그럼그럼그럼그럼 테스트를테스트를테스트를테스트를 잘잘잘잘 한한한한 걸까걸까걸까걸까????

테스트 (코드) 작성 팁

1. 요구사항을 명확하고 온전하게 이해하라2. 테스트 케이스 ID는 의미있고 쉽게 이해가능해야 한다3. 하나의 테스트 케이스에서는 하나의 흐름만을 테스트 하도록 해라4. 결함은 경계에 서식한다.5. 테스트의 목적과 한계를 명확히 이해하고 테스트 케이스를 작성하라.6. 테스트 케이스를 통해 제품이 어떻게 동작하는지를 서술하라.7. 누구나 이해 가능하도록 쉬운 테스트 코드를 유지하라8. 테스트 코드 자체만으로 어떤 내용을 테스트 하려는지 표현하라9. 작은 변경에 대해서도 이 변경을 알려 줄 수 있는 테스트 코드를 작성하라10. 에러 발견시 발생 위치, 대략적 원인을 제공해 줄 수 있도록 테스트 코드를작성하라.

11. 테스트 결과는 사람이 아닌 코드가 검증하도록 하라12. assert문을 제대로 이해하고 강화하라13. 단순히 코드를 반복하지 않기 위하여 테스트 라이브러리를 만들지 마라.14. 테스트 외부와 내부를 구분하라

1. 요구사항을 명확하고 온전하게 이해하라테스트해야 하는 정황을 이해하고 테스트 요건을 도출한다테스트 요건이 분석되면 다양한 접근법으로 테스트 케이스를 도출할 수 있다

사례) 테스트 요건 등을 테스트 코드의 주석으로 작성하면서 테스트 하려는바와 테스트를 하는 방법에 대해 고민할 수 있다

테스트 케이스 설명

2. 테스트 케이스 ID는 의미있고 쉽게 이해할 수 있어야 한다테스트 코드는 계속 수행된다. 나중에 보더라도 이 테스트가 무엇이고무슨 의도를 갖고 있는지 바로 파악할 수 있어야 한다

사례) 의미없는 숫자 시퀀스보다는 의미있는 이름으로 수정한다testGetNewLandmarkList_0208_07 -> testGetNewLandmarkList_invalidFilterType

사례) 코드 상의 테스트케이스 명을 한글로 작성할 수도 있다test_잘못된아이디로로그인시도하는경우

3. 하나의 테스트 케이스에서는 하나의 흐름만을 테스트 하도록 해라하나의 테스트 케이스에 검증문(assert)이 1개여야 한다는 말이 아니다

검증하려는 의도(목적)가 1개여야 테스트가 실패했을 때 직관적으로문제상황을 이해하고 원인을 유추할 수 있다

TC1

TC2

TC3

AA AA AA AA 테스트테스트테스트테스트 : : : : 1) 1) 1) 1) 테스트테스트테스트테스트 시작시작시작시작2) 2) 2) 2) 가에가에가에가에 대한대한대한대한 결과결과결과결과 확인확인확인확인3) 3) 3) 3) 다시다시다시다시 테스트테스트테스트테스트4) 4) 4) 4) 나에나에나에나에 대한대한대한대한 결과결과결과결과 확인확인확인확인5) 5) 5) 5) 다시다시다시다시 테스트테스트테스트테스트6) 6) 6) 6) 다에다에다에다에 대한대한대한대한 결과결과결과결과 확인확인확인확인7) 7) 7) 7) 테스트테스트테스트테스트 종료종료종료종료

4. 결함은 경계에 서식한다문자 : null, 빈 문자, 공백문자, 허용 자릿수보다 긴 문자열, 예약어, 다국어숫자 : 음수, 0, 하한값 -1, 상한값 +1, 숫자가 아닌 표시, + 기호로 시작하는 값코드값 : 유효하지 않은 코드값위,경도값 : 90.도 180.도 이상 이하의 값페이지값 : 존재하지 않는 페이지날짜 : 유효하지 않은 년,월,일, 날짜 포멧에 어긋나는 날짜값, 디스카운트 % : 음수 %값, 100% 이상……

0 100

다양한테스트기법을알아두면테스트를더잘할수있다예) 상황에따라경계값분석, 동등분할등

5. 테스트의 레벨에 따른 목적과 한계를 이해한다단위 테스트에서 통합 테스트의 내용을, 통합 테스트에서단위 테스트의 내용을 테스트하지 말라.

외부적인 상황은 배제하고 단위 모듈 자체에 대해꼼꼼이 테스트한다

꼼꼼한 테스트, 중복되는 테스트가 아니라통합 흐름에 대해 집중한다

중복된 테스트나 목적에 어긋난 테스트가아닌 통합되는 모습에 따른테스트를 수행한다

6. 테스트 케이스를 통해 제품이 어떻게 동작하는지를 서술한다특정 조건, 특정 상황에서 제품이 어떻게 동작하는지를 표현하는테스트는 동료 개발자, 사용자에게 제품 스펙 이상의 설명서가 될 수 있다

사례) 아래의 테스트 코드는 실제 실행해 보지 않아도 제품이 어떻게동작하는지 정보를 제공한다

입력값

기대값

7. 누구나 이해 가능하도록 쉬운 테스트 코드를 유지하라.기존에 작성되어 있던 테스트 코드의 불필요한 주석, 쓰이지 않는 코드,

적절하지 않은 메소드 명 등을 모두 정리하라

테스트 코드는 개발코드와 다르다. 과도하게 사용된 테스트 라이브러리(자동 검증 기능, 자동 테스트 기능 등) 가 있다면 테스트 코드에 직접 표기하라

사례 1) 테스트 결과를 자동으로 검증해 주는 유틸 클래스 사용

-> 테스트 결과가 어떻게 동작해야 하는지 알기 어렵다.

사례 2) 테스트를 자동으로 수행하는 클래스

-> 어떤 테스트 데이터로 테스트를 수행하는지 알기 어렵다.

8. 테스트 코드 자체만으로 어떤 내용을 테스트 하려는지 표현하라의미있는 테스트 케이스명을 사용한다.

의미 전달을 위해서 의미있는 변수명을 선언하고 테스트에 사용한다

적절히 한 줄을 띄어 쓴다

사례) Max값보다 큰 min 값에 대한 테스트의 경우

. 케이스명 : biggerMinLongitudeNormal,

. 변수명 : biggerThanMaxMinLongitude,

biggerThanMaxMinLatitude

9. 작은 변경에 대해서도 이 변경을 알려 줄 수 있는 테스트 코드를작성하라프로그램이 변경되면 테스트 케이스(코드)도 수정해야 하도록

테스트 케이스를 작성하라

프로그램을 변경했는데도 테스트 코드를 수정할 필요가 없다면누락된 테스트가 있을 가능성이 높다

사례) 프로그램이 변경 되었는데도 테스트 코드에는 아무런 변화가 없었다기존 코드를 분석한 결과 테스트 수행 후 결과값을 검증하는 부분이 빈약했었고, 이 부분을 강화하자 변경으로 인해 발생한 결함이 발견되었다

10. 에러 발견시 발생 위치, 대략적 원인을 제공해 줄 수 있도록 테스트코드를 작성하라

사례 1) 테스트 결과를 자동으로 검증해 주던 테스트 라이브러리를 제거하고, 각 테스트 코드별로 테스트 결과를 검증하도록 하여 어떤 검증에서 실패했는지라인이 표시되도록 하고, 실패 메시지도 각 상황에 대해 별도로 표시되게 했다

사례 2) 테스트 코드 작성할 때 assert문에 실패 시 표시되는 메시지를 추가한다[ assertXXX(“실패할때만 같이 표시되는 메시지”, ~기존 검증 구문~) ]

사례 3) 테스트 라이브러리에서 에러 발생시 해당 케이스 index, 입력값, 변수명, 기대값과 실제 값 등을 제공하도록 하였다.

사례 4) setUp, tearDown에서의 에러(TestSetupException)와 테스트 수행 과정의 에러를 별개로 구분해서 테스트 실패 시 원인 파악이 잘 되도록 도왔다

사례 5) 테스트 ‘에러’와 ‘실패’를 구분해서 에러는 테스트 수행 중에 예상하지 못한오류가 발생한 것, 실패는 테스트 수행 결과가 최초 작성한 것과 다른 경우로상황을 분리했다

11. 테스트 결과는 사람이 아닌 코드가 검증하도록 하라사례 1) 테스트 수행 결과값을 system.out.println 을 표시하던 부분을

assert문으로 대체System.out.println(“결과값 : ” + 결과값);assertEquals(“결과값이 기대값과 다릅니다.”, 기대값, 결과값);

사례 2) 상황에 알맞게 assert문을 작성한다

*assertEquals -같은지비교

*assertNull - null값을리턴하는지비교

*assertNotNull -인자로넘겨받은객체가 null인지판정하고반대인경우실패로처리한다.

*assertSame - assertSame은 expected 와 actual이같은객체를참조하는지판정하고

그렇지않다면실패로처리한다.

*assertNotSame - expected 와 actual이서로 '다른' 객체를참조하는지판정하고, 만약

같은객체를참조한다면실패로처리한다.

*assertTrue - boolean조건이참인지판정한다. 만약조건이거짓이라면실패로처리한다.

* fail -테스트를바로실패처리한다.

12. assert문을 제대로 사용하라- 의외로 assert문이 늘 성공하도록 작성된 테스트 코드가 많다

if( A != null) {assertNotNull(A);

}else{assertNull(A);

}

사례 1) 테스트 결과를 System.out 하기만 하고 결과를 검증하지 않는 코드도 있다

사례 2) List 조회의 경우 실제 조회된 건이 없는데도 assert문이 누락된 경우가 있다.List resultList = 수행결과…;assertNotNull(resultList);

// assertTrue(resultList.size() > 0); 실제로는 조회된 건이 없어서 테스트 실패

13. 단순히 코드를 반복하지 않기 위하여 테스트 라이브러리를 만들지마라

- 테스트 코드는 개발코드가 아니다. 코드가 반복되는 상태로 그대로 두는방식(오픈 코딩)을 유지하라

별도 라이브러리를 사용하는 테스트는 검토하기도 어렵고, 디버그도 어렵고, 다시 수정하기도 어렵다

사례 1) 테스트 결과를 자동으로 검증해 주는 유틸 클래스 사용-> 테스트가 실패하는 경우 원인을 직관적으로 알수 없고, 디버그하기도 어렵다.

사례 2) 테스트를 자동으로 수행하는 클래스-> 어떤 테스트 데이터로 테스트를 수행하는지 알기 어렵다.

14. 테스트 외부와 내부를 구분하라.- 테스트 실행과 사전조건/사후 행동을 구분하라- 테스트 데이터 세팅부분과 테스트 실행을 구분하는 것도좋은 접근이다

- 테스트를 이해하기 쉽고, 검토하기가 쉬워진다- 별도의 테스트 사례 생성기를 적용할 수 있어진다- 직관적으로 버그를 파악할 수 있다

setUp

test

tearDown

setUpException

tearDownException

** Junit 수행 결과 **SuccessSuccessSuccessSuccess : 테스트 성공ErrorErrorErrorError : 테스트 수행과정에서 예상하지 않은 Exception 발생FailFailFailFail : 테스트 수행 결과가 기대한 결과(assert문 실패)와 다른 경우

추가 - 대규모 프로젝트에서 Junit 수행 팁

가) 테스트 데이터, 공통 기능 공유

나) 테스트 전체에 대한 설정 일원화(예: 테스트 수행 서버의 세팅)

다) DB 조회(쿼리 수행)를 위한 테스트 유틸리티 소개

라) ValueSetVO를 통한 반복적인 유효성 테스트 소개

마) 입력 값에 대한 일반적인 유효성 테스트 소개

바) Exception, Junit의 fail/Error를 통한 오류 직관성 강화

사) 모듈화를 통한 비즈니스 테스트

가. 테스트 데이터, 공통 기능 공유- 테스트 데이터를 별도 클래스에 분리하여 사용

CommonTestData공통 기능

CommonTestData공통 기능

AServiceTestDataA의 유용한 실행코드

AServiceTestDataA의 유용한 실행코드

BServiceTestDataB의 유용한 실행코드

BServiceTestDataB의 유용한 실행코드

CServiceTestDataC의 유용한 실행코드

CServiceTestDataC의 유용한 실행코드

AAAA

BBBB

CCCC

나. 테스트 전체에 대한 설정 일원화(예: 테스트 수행 서버의 세팅)

XXTestCase

AServiceTest BServiceTest CServiceTest

공통 설정용 변수 정의

대상 서버수행 브라우저수행, 성공 기준테스트용 프레임워크 설정 파일명

그대로 사용

다. DB 조회 모듈 등의 테스트 유틸리티 소개테스트 코드 중간에 직접 쿼리를 입력해 DB 조회가 가능한 유틸리티를

제공하여 동적인 테스트 지원

현재는 이렇지만나중에는?

테스트작성

DBAccessManagerDBAccessManager

~~~~~~~~

DBDBDBDB

라. 반복적인 유효성 테스트를 위한 라이브러리 작성

(사례) RESTful OpenAPI에 대해 입력 값의 타입별 반복적인 테스트 지원

null 빈문자빈문자빈문자빈문자 공백문자공백문자공백문자공백문자정해진정해진정해진정해진길이길이길이길이이상의이상의이상의이상의입력값입력값입력값입력값

특수문자특수문자특수문자특수문자,한글한글한글한글/한자한자한자한자

개발된개발된개발된개발된 제품제품제품제품

대상 변수

테스트데이터

기대값

테스트 수행

junit.framework.AssertionFailedError:

(Test Failed) Details : categoryName

[ 2] Input value: , Expected code: CCT_1000,

but Returned code : 200

[ 3] Input value: 123456789, Expected code:

CCT_1002, but Returned code : 200

[ 5] Input value: 12345678901, Expected

code: CCT_1002, but Returned code : 200

3 case(s) failed.

테스트 결과

마. 일반적 유효성 테스트 케이스 소개

. 코드성 데이터 : 코드값 이외에 대한 테스트

. Date 타입 : 존재하지 않는 년, 월, 일에 대한 테스트, 시작/종료일 간의 선후 테스트

. 위도/경도, 각도(longitude, latitude, radius) : +-90도, +- 180도 초과/미만 값

등에 대한 테스트

. Description 테스트 : 전세계 서비스 특징에 따라 한글, 한자 등에 대한 테스트

. List 조회 테스트 : 정렬 필드, 필터링 문자식, pageNo, countPerPage 등에 대한 테스트

. 정수형, 더블형 등 : 음수, 0 , 주어진 타입의 최대값 등에 대한 테스트

. List, array 형태 : null, 주어진 데이터 없음 등에 대한 테스트

여기까지.

중구난방, 두서없이사례를 든 코드가 오래 전 코드 (JUnit3… 쿨럭)

그럼에도 불구하고 전하려던 메시지는…………..테스트는 한 거 자체가 중요한게 아니라

어떻게 했느냐가 중요하다

“좋은 테스트케이스”

좋은 테스트 케이스는

다른 사람이 작성했더라도 수행하는 사람이

각 단계를 즐겁게 따라할 수 있는 케이스다.

테스트 케이스는

여러 사람에 의해 지속적으로 수행될 때

그 효용성이 극대화되기 때문이다.

http://qualitypoint.blogspot.com/2009/05/writing-good-test-cases-and-finding.html

감. 사. 합. 니. 다.