이렇듯 JUnit은 모든 테스트를 독립적으로 만듭니다(테스트끼리의 결합성을 낮춥니다). 그렇지 않다면 테스트의 실패 이유를 찾는것이 너무 어려워지기 때문입니다. 따라서 테스트 클래스에서는 static 필드를 사용하지 않습니다
JUnit에서 단언은 테스트에 넣을 수 있는 정적 메소드 호출입니다. 각 단언은 어떤 조건이 참인지 검증하는 방법입니다. 단언한 조건이 참이 아니면 테스트는 즉시 종료하고 실패를 보고합니다(또한 JUnit은 catch하지 않은 exception에 대해서 error로 말합니다)
assertTrue는 true냐 false냐에 대해 단언합니다. 하지만 테스트코드는 검증하는 기댓값을 명시적으로 지정하는 것이 낫습니다
@Test
public void depositIncreaseBalance(){
//account는 @Before을 통해 초기화 필요
int initialBalance = account.getBalance();
account.deposit(100);
assertTrue(account.getBalance() > initialBalance);
}
대부분 단언은 기대하는 값과 반환된 실제 값을 비교합니다. 단지 잔고가 0보다 크다고 하기보다는 100과 같은지를 비교합니다
@Test
public void depositIncreaseBalance(){
//account는 @Before을 통해 초기화 필요
int initialBalance = account.getBalance();
account.deposit(100);
assertThat(account.getBalance(), equalTo(100));
}
assertThat() 정적 메소드는 햄크레스트 단언의 예입니다. 햄크레스트 단언의 첫 번째 인자는 실제 표현식, 즉 우리가 검증하고자 하는 값입니다. 두 번째 인자는 매처입니다. 매처는 실제 값과 표현식의 결과를 비교합니다.
또한 assertThat은 fail 시 expected : <100> but : was <101>와 같은식으로 쓸모있는 에러메시지를 반환합니다. 하지만 assertTrue는 fail 메시지만 반환할 뿐이므로 추가적인 디버깅이 필요합니다
assertThat(account.getName(), is(notNullValue()));
. . .
위의 코드에서 name이 null을 반환한다면 아래의 코드는 동작하지 않습니다. 이때, 예외를 던지는 null 참조 예외는 텍스트 오류가 발생하며 테스트 실패는 발생하지 않습니다.
추가적으로 때에 따라 4번째 단계가 필요할 수도 있습니다
테스트를 작성할 때는 클래스 동작에 집중해야 하며 개별 메소드를 테스트한다고 생각하면 안됩니다
예를 들어, 은행의 ATM 클래스를 생각해봅시다. 클래스의 메소드에는 deposit(), withdraw(), getBalance()가 있습니다
makeSingleDeposit, makeMultipleDepoist 테스트가 있다고 할 때, 각 테스트 결과를 검증하려면 getBalance() 메소드를 호출해야합니다. 하지만 getBalance()를 테스트 하는게 의미가 있을까요? getBalance()는 단지 객체의 필드를 반환하기만 하는 메소드이기 때문입니다. 테스트 대상이 될만한 동작에는 입금과 출금 같은 다른 동작이 나와야 합니다. 예를 들면 makeSingleWithdrawl, makeMultipleWithdrawls, attemptToWithdrawTooMuch 등과 같이 말이죠.
단위 테스트를 작성할 때는 먼저 전체적인 시각에서 시작해야 합니다. 개별 메소드를 테스트하는 것이 아니라 클래스의 종합적인 동작을 테스트해야 합니다
테스트는 주어진 프로젝트 안에서 프로덕션 코드와 분리해야 합니다. 테스트는 프로그래머만 하는 활동이고, 고객이나 사용자는 프로덕션 코드를 실행하기만 합니다.
따라서 테스트 코드는 프로덕션 시스템 코드에 의존하지만, 반대는 아닌 일방향 관계입니다.
대부분 소프트웨어를 배포할 때 테스트를 제외한 채 베포합니다. 왜냐하면 테스트를 포함할 경우 JAR 파일이 부풀려지고(성능 저하), 코드 베이스의 공격 표면도 늘어나기 때문입니다.
단위 테스트는 문서의 역할을 해야 합니다. 따라서 테스트의 이름에 더 많은 의미를 부여할 수 있습니다. 테스트하려는 맥락을 제안하기보다는 일련의 행동을 호출했을 때 어떤 결과가 나오는지를 명시해야 합니다
양식은 doingSomeOperationGenerateSomeResult(어떤 동작을 하면 어떤 결과가 나온다)
또는 someResultOccursUnderSomeCondtion(어떤 결과는 어떤 조건에서 발생한다)
혹은 BDD의 given-when-then 양식을 따라 givenSomeContextWhenDoingSomeBehaviorThenSomeResultsOccurs(주어진 조건에서 어떤 일을 하면 어떤 결과가 나온다)
여기서 givenSomeContext를 제외하여 whenDoingSomeBehaviorThenSomeResultOccurs(어떤 일을 하면 어떤 결과가 나온다) 등으로 표현합니다