Clean Software/Testing

테스트 코드 작성을 위한 가이드라인 : FIRST, Right-BICEP, CORRECT

728x90
반응형

 

 

0. 어떤 테스트를 작성할지 결정

  • 테스트는 보통 반복문 if문 등 조건문을 보는것으로 출발한다.
  • 그 이후 데이트 변형을 고려하고 데이터가 null 혹은 0일경우도 대비한다.
  • 또한 데이터 값은 조건문을 평가하는데 어떤 영향을 주는지 살핀다.
  • 종속적인 조건들은 테스트 하나로 묶을 수 잇다.
  • Junit은 테스트할때마다 새로운 인스턴스를 생성한다.(모든 테스트는 독립적으로 만든다)

1. AAA로 일관성 유지

  • 준비(Arrange)
  • 실행(Act)
  • 단언(Assert)

2. 메서트 테스트가 아닌 동작을 테스트

  • 단위 테스트를 작성할 때는 전체적인 시각에서 테스트를 해야한다.
  • 개별 메서드를 태스트하는것이 아니라 클래스의 종합적인 동작을 테스트해야한다.

3. 테스트 코드와 프로덕션 코드

  • 테스트 코드는 프로덕션 코드에 의존하지만, 그 반대는 아니다
  • 테스트 코드와 프로덕션 코드는 같은 패키지, 다른 디렉토리에 배치한다. (테스트 클래스는 패키지 수준의 접근권한을 갖는다. )
  • 테스트를 작성할때 프로덕션코드의 공개 인터페이스만 사용해야한다. 만약 내부 행위를 테스트하려한다는 것은 설계(단일책임원칙)가 잘못됬을 확률이 높다 (-> 해결책은, 테스트하려던 내부 행위 private 메서드를 추출하여 다른 클래스로 이동시킨다)

4. 테스트는 단일 목적을 갖는다

  • 테스트는 각각 별도의 JUnit 테스트메서드로 분리해야한다.
    • 그래야 각각의 검증하는 동작을 표현하는 이름을 붙여, Assert가 실패했을 때 이름을 보고 어느 동작에서 문제가 있는지 빠르게 파악할 수 있다. 또한 실패한 테스트를 독립적으로 파악할 수 있게 된다.

5.  문서로서의 테스트

테스트 이름은 테스트하려는 맥락을 제안하기보다는, 어떤 맥락에서 일련의 행동을 호출했을 때, 어떤 결과가 나오는지 명시해야한다.

  • 테스트 이름 양식
    • doingSomingOperationGenerateSomeResult (어떤동작을 하면, 어떤 결과가 나온다)
    • somResultOccursUnerSomeCondition (어떤 결과는 어떤 조건에서 발행한다.)
  • 예시 bad -> good
    • makeSingleWithdrawal -> withdrawalReducesBalanceByWithdrawnAmount
    • attemptToWithdrawTooMuch -> withdrawalOfMoreThanAvailableFundsGeneratesError
    • multipleDeposits -> multipleDepositsIncreaseBalanceBySumOfDeposits
  • 테스트 이름 개선방법
    • 로컬 변수 이름 개선하기, 의미있는 상수 도입하기, 햄크레스트 단언 사용하기, 커다란 테스트를 작게 나누어 집중적인 테스트 만들기, 테스트 군더더기들을 도우미 메소드와 @Before 메서드로 이동하기

6. @Before, @After, @BeforeClass, @AfterClass

JUnit은 소스코드에 있는 것과 무관한 순서로 테스트 메서드를 실행한다.

 

만약 특정 클래스에 다음과 같은 JUnit 메서드가 있다고 할때

@Before aMethod()

@Before bMethod()

@Test cMethod()

@Test dMethod()

  • cMethod()메소드와 dMethod() 메소드 실행 전에는 각각 aMethod() bMethod() 메소드가 모두 호출된다
    • 가능한 실행순서
      1. @Before aMethod()
      2. @Before bMethod()
      3. @Test cMethod()
      4. @Before aMethod()
      5. @Before bMethod()
      6. @Test dMethod()
  • 이때 @Before 메소드 끼리의 순서는 보장되지 않는다. aMethod()가 먼저 호출될수도 있고 bMethod()가 먼저 호출될수도 있다 (따라서 일정한 순서가 필요하다면 단일 @Before 메소드로 묶어야함)
    • 가능한 실행순서
      1. @Before aMethod()
      2. @Before bMethod()
      3. @Test dMethod()
      4. @Before bMethod()
      5. @Before aMethod()
      6. @Test cMethod()
  • 드물게 테스트 후를 정리하는 @After 클래스를 사용할 때도 있다
  • @BeforeClass, @AfterClass도 드물게 사용된다
  • 미완성 혹은 필요없는 테스트는 @Ignore 로 테스트 주석처리한다.

 

 


좋은 테스트의 FIRST 속성

다음 5가지 원리(FIRST: 빠르고, 고립시키며, 반복가능하고, 스스로 검증도 가능하고, 적시에 사용하는 테스트)를 따르며

테스트를 작성하면, 테스트를 먼저 작성하든 나중에 작성하던 많은 이점을 얻을 수 있다. 


1. Fast : 빠르다

코드만 실행하는 테스트는 빠르지만, 데이터베이스, 파일, 네트워크 호출처럼 외부자원을 다루는 코드를 호출할때는 실행시간이 느려진다.

전형적인 자바시스템은 단위테스트가 수천개 인데 평균2초만 걸려도 단위테스트2500개를 실행하는데 8분이 걸린다. 

단위테스트를 하루에 세번 실행하기도 버겁다면 잘못된 방향으로 가고있는 것이다.

 

DB 호출과 같은 느린테스트에 대한 의존성을 줄이자.

 

2. Isolated : 고립시킨다.

 

 

3. Repeatable : 반복 가능하다.

 

 

4. Self-validating : 스스로 검증 가능하다.

 

 

5. Timely 적시에 사용한다.

 

 


무엇을 테스트 할 것 인가? : Right-BICEP 

테스트는 최상의 시나리오(행복경로: 소프트웨어 최종 사용자 목표를 반영하는 긍정적인 사례) 가 통과하는지 테스트하는게 기본이고,

특히 문제가 발생할수 있는 부분(경계부분: 결함/버그가 발생할 가능성이 높은 사례)을 테스트 케이스로 만들고 통과하는지 여부를 테스트해야 그 문제가 해결되도록 코드가 작성되었는지 확인할 수 있다. 

 

경험이 쌓이면 느낌만으로 문제가 있을수 있는 부분을 찾아 집중해 테스트할수 있겠지만,

그렇지 않은경우 다음 Right-BICEP 지침을 통해 무엇을 테스트 하는것이 중요한지 알 수 있게된다.


Right 결과가 올바른가?

만약 어떤 작은부분의 코드에 대해 행복경로 테트스를 할 수 없다면 그 내용(소프트웨어의 최종 사용자 목표)을 완전히 이해하지 못한것이다. 

 

Boundary conditions(경계조건)은 맞는가?

 

Inverse relationship(역관계)를 검사할 수 있는가?

 

Cross-check(교차검사)를 다른 수단을 활용하여 할 수 있는가? 

 

Error conditions(오류 조건)을 강제로 일어나게 할 수 있는가?

 

Performance characteristics(성능 조건)은 기준에 부합하는가?

 


경계 조건(Boundary Condition):  CORRECT

경계조건은 행복경로의 끝에 있는 것으로 자주 문제가 발생한다.

이런 결함들을 미연에 방지하기 위해서는 경계조건들과 관계된 테스트케이스를 작성해야 한다. 

 

다시말해 적어도 이거까지(경계조건) 참으로 통과한다면 그 기능은 제대로 작동한다고도 볼수 있다고 생각하는것이다. 

computer science는 말 그대로 math(수학)가 아닌 science(과학)이기 때문에 무언가 올바른지 입증할때 유클리드 방식과 같은 수학적인 증명이 아닌, 반증을 통해 증명하는 방식으로 동작한다.

각고의 노력으로도 반례를 들 수 없는 서술이 있다면 목표에 부합할 만큼은 참이라고 보는 것이다. 

 

따라서 테스트는 엄밀히 따지면 '버그가 있는 것을 보여줄수 있고, 버그가 없다는 것을 보여줄 수는 없다.'

그래서 테스트에 충분한 노력을 들여 테스트가 보장할 수 있는 것은,

프로그램이 목표에 부합할 만큼은 충분히 참이라고 여길 수 있게 해주어야 한다는 것이다. 

 

따라서 잘못될 수 있는 다양한 시나리오를 오랫동안 상상하고 분석하여

다양하고 충분히 테스트케이스를 작성하는게

해당 소프트웨어가 얼마나 참인지를 보여줄 수 있는 것이다. 

 

더 읽어보기

정말 "도움이 되는" 테스트여야만 도움이 되는 것이다. "도움이 같은 " 오히려 독일 수도 있다.


Conformance(준수)

 

Ordering(순서) : 

 

Range(범위) : 

 

Reference(참조) : 

 

Existence(존재) : 

 

Cardinality(기수, 중복성이 없는 정도) : 

 

Time (절대적 혹은 상대적 시간) : 

728x90
반응형

'Clean Software > Testing' 카테고리의 다른 글

Hamcrest (읽기 쉬운 JUnit Test 코드 작성하기)  (0) 2021.04.28
JUnit 5 (JUnit 4와 비교)  (3) 2021.04.28
TDD 테스트 주도 개발  (0) 2021.04.28