[JUnit] Assert에 대해 알아보자

[JUnit] Assert에 대해 알아보자

📝Assert

assertTrue

org.junit.Assert.assertTrue(someBooleanExpression);

단언은 JUnit 테스트에서 자주 사용되기 때문에 대부분 군더더기를 줄이고자 정적 import를 사용한다

import static org.junit.Assert.*;

//사용 예시
@Test
public void hasPositiveBalance(){
    account.deposit(50);
    assertTrue(account.hasPositiveBalance());
}

위에서 Account 인스턴스가 초기화 되어 있어야 한다.

@Before 메서드에서 Account 객체를 생성하고 참조를 테스트 클래스의 필드로 저장하면 된다.

assertThat

assertTrue가 참과 거짓을 구분한다면 assertThat은 명확한 값을 비교하는데 사용된다.

assertThat(account.getBalance(), equalTo(100));

햄크레스트 단언의 첫 번째 인자는 실제표현식 즉 우리가 검증하고자 하는 값이다.

두 번째 인자는 매처(matcher)이다. 매처는 실제 값과 표현식의 결과를 비교한다. 매처는 테스트 가독성을 크게 높여준다는 장점이 있다.

햄크레스트 매처를 사용하려면 코드에 정적 임포트를 추가해줘야한다.

import static.org.hamcrest.CoreMatchers.*;

equalTo 매처에는 어떤 자바 인스턴스나 기본형 값이라도 넣을 수 있다. equalTo매처는 비교 기준으로 equals()메서드를 사용한다.

자바 기본형은 객체형으로 오토 박싱 되기 때문에 어떤 타입도 비교할 수 있다.

일반적인 단언보다 햄크레스트 단언이 실패할 경우에 오류메세지에서 더 많은 정보를 알 수 있다. 앞의 테스트에서 기대값은 100인데 만약 101이 반환되면 오류메시지가 아래와 같이 나타난다.

//assertThat의 에러메시
java.lang.AssertionError:
Expected: <100> 
    but: was<101>

//전통적인 방식의 assertTrue의 에러메시지
...
java.lang.AssertionError
    at org.junit.Assert.fail(Asser.java:86)
...

따라서 assertTrue()보다 자세한 스택 트레이스를 보고 싶다면 다음과 같이 작성하면된다.

account.desposit(50);
assertThat(account.getBalance() > 0, is(true));

중요한 햄크레스트 매처 살펴보기

자바 배열 혹은 컬렉션 객체를 비교할 때는 equalTo() 메서드를 사용하며, 예상한대로 작동한다.

아래 에시를 보자

//실패하는 단언
assertThat(new String[] {"a", "b", "c"}, equalTo(new String[] {"a","b"}));
assertThat(Arrays.asList(new String[] {"a"}), equalTo(Arrays.asList(new String[] {"a","ab"})));

//통과하는 단언
assertThat(new String[] {"a", "b"}, equalTo(new String[] {"a", "b"}));
assertThat(Arrays.asList(new String[] {"a"}), equalTo(Arrays.asList(new String[] {"a"})));

//is를 사용해 가독성을 높인 단언 ~~취직하고싶다~~
Account account = new Account("want to get a job");
assertThat(account, is(equalTo("want to get a job")));

//부정할 때에는 not매처를 사용한다
assertThat(account.getName(),not(equalTo("i already have a job")));

//null 값을 검사할 때
assertThat(account.getName(), is(not(nullValue())));

경우에 따라 is 장식자를 추가하여 매처표현의 가독성을 더 높일 수도 있다. is는 단지 넘겨 받은 매처를 반환할 뿐이다.


🎲부동 소수점 비교

컴퓨터는 모든 부동소수점 수를 표현할 수 없기 때문에 부동소수점 타입(float, double 등)은 근사치로 구해야 한다. 단위 테스트에서 시사점은 두 부동소수점 수를 비교해도 항상 원하는 대로 나오지 않을 수 있다는 것이다.

//테스트를 통과할 것 같지만 실패하는 코드
assertThat(2.32 * 3, equalTo(6.96));

//아래처럼 에러가 나온다
java.lang.AssertionError:
Expected: <6.96>
but: was <6.599999999999>

따라서 두개의 float 혹은 double을 비교할 때는 허용 오차를 지정해야 한다. assertTrue()을 사용하여 다음과 같이 작성할 수 있다. 하지만 가독성이 별로다. 그래서 isCloseTo라는 햄크레스트 매처를 사용할 수 있다. 이 매처는 closeTo()정적 메서드를 제공한다.

//매처를 사용하지 않은 부동소수점 비교
assertTrue(Math.abs(2.32*3) - 6.96) < 0.0005);

//매처를 사용해 가독성을 높인 부동소수점 비교 단언
import static.org.hamcrest.number.IsCloseTo.*;
assertThat(2.32*3, closeTo(6.96, 0.0005));

하지만 가독성이 별로다.


🗨️단언 설명

모든 JUnit단언의 형식(전통적 fail(), 햄크레스트 assertThat()) 에는 message라는 선택적 첫 번째 인자가 있다. message 인자는 단언의 근거를 설명해 준다.

@Test
public void testWithWorthlessAssertionComment(){
    account.deposit(50);
    assertThat("account balance is 100", account.getBalace(), equalTo(50));
}

주석의 기대 잔고는 100 이지만 실제 코드의 기댓값은 50이다. 설명이 있는 주석문을 선호한다면 단언에 이처럼 메시지를 추가할 수 있다. 하지만 더 좋은 방법은 테스트를 코드 자체만으로 이해할 수 있게 작성하는 것!

참고

햄크레스트 매처에 대해 더 알아보고싶다면 API문서를 확인해보자

CoreMatchers (Hamcrest)

컴퓨터에서 부동소수점을 정확하게 구할 수 없는 이유 Why can't decimal numbers be represented exactly in binary?