[Junit] 경계조건: CORRECT


에러냐 아니냐의 경계가 되는 조건을 경계조건이라고 한다.(ex. a 나누기 b의 경계조건은 b>0 )

이러한 경계 조건을 생각할때 고려해야할 7가지(CORRECT)를 알아보자.

  • [C]onformeance(준수)
  • [O]rdering(순서)
  • [R]ange(범위)
  • [R]eference(참조)
  • [E]xistence(존재)
  • [C]ardinality(기수)
  • [T]ime(시간)

1. Conformance (준수)

데이터가 특정 양식을 따르는지 검증해야함.

ex) 이메일의 경우, name@somedomain 의 양식을 따르는가?

각 경계조건이 발생했을때 어떤 일이 일어나는지 보여줄 수 있는 테스트코드를 작성해야함. (ex. @기호가 없다거나? name이 없다거나?)

구조적 데이터의 경우 모든 테스트케이스를 조합하면 수가 너무 많을 수 있음. 필드가 처음 입력될때 UI단에서 검증하는 로직이 있다면, 굳이 그 필드가 인자로 넘어올때마다 검사할 필요는 없음. 시스템의 데이터 흐름을 이해하면 불필요한 검사를 최소화할 수 있다.

2. Ordering (순서)

데이터 순서 혹은 컬렉션에서 특정 데이터의 위치등이 사용되는 코드는 쉽게 잘못될 수 있어 특히 주의해야함. 가령 점수가 높은 학생 순서로 리턴하는 프로그램이 있다면?? 학생 리스트를 점수 내림차순으로 정렬하여서 리턴하는지 잘 살펴야함. 오름차순으로 정렬하여 리턴한다면 아예 반대의 프로그램이 되어버릴것.

3. Range (범위)

프로그램이 허용할 수 있는 데이터 범위를 넘는지 검증해야함. 예를들어 나이(age)를 나타내는 변수가 있다면 여기에 음수가 들어오면 안됨.. (나이가 마이너스일수는 없으니까..) 등

이런 애들은 자바 기본형으로 저장하기보다 추상화 클래스를 사용하면 좋다.

ex. 도형의 회전각도를 저장하는 변수가 있다면? 자바 기본형 저장하지말고 Bearing 클래스를 만들어 max값을 360으로 제한함.

public class Bearing{
	public static final int MAX = 359;
	private int value;
	
	public Bearing(int value){
		if (value < 0 || value > MAX) 
			throw new BearingOutOfRangeExcetion();
		
		this.value = value;
	}

	public int value(){
		return value;
	}

또한 @After 어노테이션을 사용하여 테스트가 완료되었을때마다 확인하게하여 객체의 불변식을 참으로 유지할 수 있음.

ex) 직사각형의 각 변이 100 이하라는 조건이 있을때.

@After
public void ensuerInvariant(){
	assertThat(rectangle, constrainsSidesTo(100));  // constrainsSidesTo는 햄크레스트매처를 오버라이드한것
}

인덱싱

범위문제에 해당하는 인덱싱문제도 많은 잠재적 오류를 포함하고있음. 아래 인덱스를 다룰때 고려해야할 몇가지 테스트 시나리오를 첨부함.

  • 시작인덱스 ≠ 마지막 인덱스
  • 시작인덱스 < 마지막인덱스
  • 인덱스 > 0
  • 인덱스 < MAX
  • 사이즈 = 실제 항목의 개수

4. Reference (참조)

어떤 메서드들은 아래와 같은 참조사항을 고려해야함

  • 범위를 넘어서는것을 참조하고있는가?
  • 외부 의존성이 있는가?
  • 특정 상태에 있는 객체를 의존하고있는가?
  • 반드시 존재해야하는 조건들이 있는가?

ex) 고객의 계정 히스토리를 표시하는 웹앱은 고객의 로그인이 선행되어야함.

ex) 스택의 pop() 메서드를 호출하기 위해서는 스택이 비어있으면 안됨.

위와같이 메서드의 사전조건(precondition)들을 잘 검사하도록 하자.

5. Existence (존재)

스스로에게 ‘주어진 값이 존재하는가?’를 물어봄으로써 많은 잠재적 결함을 발견할 수 있음. 어떤 인자를 허용하거나 필드를 유지하는 메서드에 값이 null / 0 / 비어있는 경우에 어떤 일이 생길지 생각해보자.

자바라이브러라에서는 어떤 데이터가 존재하지 않거나 초기화되지 않은 상태로 사용하면 숨막히게 예외를 던지는 경향이 있음. 무작정 nullexception을 받으면 문제원인을 이해하기 어려울수있으나, 가령 ‘프로파일 이름이 설정되지 않음’같은 특정 메세지를 예외에서 알려주면 문제를 추적하는 과정을 단순화할수있다.

6. Cardinality (기수. 집합의 원소갯수)

어떤 컬렉션을 다룰때는 아래와 같은 갯수의 개념에 대한 테스트를 고려해야함.

  • 목록에 항목이 하나도 없을때 보고서 출력하기
  • 목록에 항목이 한개만 있을때 보고서 출력하기
  • 목록에 항목이 없을 때 한 항목 추가하기
  • 목록에 항목이 하나만 있을 때 한 항목 추가하기
  • 목록에 항목이 아직 열 개 미만일때 한 항목 추가하기
  • 목록에 항목이 이미 열 개가 있을때 한 항목 추가하기

위 예시를 보면 알겠지만 테스트코드는 0, 1, n 이라는 경계 조건에만 집중하면됨. (n은 비즈니스 요구조건에 따라 바뀔수있음)

7. Time (시간)

시간에 관해 고려해야할 테스트케이스는 다음과 같음

  • 상대적 시간 (시간 순서)
  • 절대적 시간 (측정된 시간)
  • 동시성 문제

하나씩 살펴보자

1) 상대적 시간

상태를 가지고 있는 메서드들 중에 순서가 중요한 케이스들이 있음.

ex. login() 은 반드시 logout() 메서드 앞에 호출되어야함

ex. read() 앞에 반드시 open() 이 먼저 호출되어야함. 등등

호출 순서가 맞지 않았을때 어떤 일이 일어나야할지 생각하고 테스트케이스를 짜야함.

또한 타임아웃(timeout) 문제 역시 고려해야함. 수명이 짧은 자원에 대해 코드가 얼마나 기다릴 수 있는지 결정해야함.

2) 절대적 시간

  • 타임존에 따른 문제 (UTC 인가? DST인가?)
  • 써머타임이 있는가?

같은 문제들이 있을 수 있겠다… 이러한 문제에 대한 해결책 중 하나는 시스템 타임에 의존하는 테스트를 작성하는것. 혹은 이전 장에서 설명했듯 코드안에서 Clock 객체를 받아와서 사용하는것..

3) 동시성 문제

  • 동시에 같은 객체를 다수의 스레드가 접근한다면?
  • 전역 혹은 인스턴스 수준의 데이터나 메서드에 동기화처리를 해야하는가?
  • 파일 혹은 DB 같은 외부 참조는 어떻게 처리해야하는가?

위와 같이 클라이언트에 동시성 요구사항이 있다면 다수의 클라이언트 스레드를 보여주는 테스트를 작성 할 필요가 있음.




© 2020. by berrrrr

Powered by berrrrr