본문 바로가기
개발/Test

[Test] JUnit5을 사용해보자 (2)

by solchan98 2022. 2. 10.

JUnit5

이전에 JUnit5가 무엇인지, 기본적인 사용방법을 알아보았다.
이번 포스트는 대표적인 Assertion을 통해 테스트를 진행해볼 것이다.

Assertion

테스트를 진행하기 위해선 여러가지 상황이 필요하다.

예를 들어 Account라는 객체의 age가 19미만인 경우 술과 담배를 판매하면 안되는 상황이라면 술과 담배 판매 메서드를 테스트할 때, age < 19의 경우는 판매가 되면 안되는 것을 테스트 하면된다.

위 예는 판매라는 메서드의 어떠한 경우이며, 판매가 아닌 또 다른 기능을 수행하는 메서드라면 해당 기능에 맞는 테스트를 진행해야 한다.
이를 위해 Assertion이 존재한다.
Assertion의 메서드는 꽤 많이 정의되어있다. 따라서 자주 쓰이는 메서드를 위주로 정리하였다.

자주 쓰이는 Assertion 친구들을 소개한 후, 테스트 할 수 있는 시나리오를 가정하여 해당 시나리오에 맞게 테스트를 진행해 볼 것이다.

assertEquals

assertEquals는 두 개의 인자가 같은지 비교하는 메서드이다.
기본적으로 메서드 네이밍이 어떠한 역할을 하는지 잘 설명되어있다.

assertEqulas(기대값, 실제값);

부가적인 설명이 필요 없다..

assertNotNull

assertNotNull역시 이미 메서드명을 봐도 알 수 있다.
인자가 Null이 아닌지 체크한다.

assertNotNull(실제값);

assertTrue

인자로 조건식이 들어가고 조건의 결과가 true인지 체크한다.
boolean이 들어간다고 되어있지만, 조건식이 들어가면 조건식의 연산 결과는 boolean이기 때문에 같은 말이다.

assertTrue(boolean);

assertAll

기본적으로 테스트 코드는 순서대로 실행된다.
만약 아래와 같이 테스트를 진행할 때, 첫번째 테스트가 실패한 경우 두번째 테스트인 assertTrue()는 진행되지 않는다.

assertEquals(A.name, "B");
assertTrue(A.age, 24);

하지만 우리는 한 번에 위 두 테스트의 성공 여부를 알고싶다.
이런 경우에 사용되는 것이 assertAll이다.

사용 방식은 다음과 같다.

assertAll(
    () -> assertEquals(...),
        () -> assertTrue(...), 
        () -> assertNotNull(...)
        );

이렇게 인자로 람다식을 여러개 넘겨주면 동시에 테스트가 수행된다.
람다식을 이렇게 넘겨줄 수도 있고, 다른 방식으로 넘겨줄 수 있다.
자세한 방식을 알고싶은 경우 Assertions.class를 열어보면 확인할 수 있다.

assertThrows

assertThrows는 로직에서 예외가 발생하는 경우를 테스트 하고싶을 때 사용된다.
처음에 예로 들었던 19세 미만에게는 담배와 술을 판매할 수 없는 경우를 보자.
19세 미만이 구매 요청을 하면 예외 처리를 해야한다.
아래 테스트는 18세 친구가 구매 요청을 하는 경우이고, IllegalArgumentException이 발생해야 한다.

@DisplayName("19세 미만 술, 담배 구매 예외 테스트")
void notSaleTest() {
    assertThorws(IllegalArgumentException.class, () -> 마트.술담배구매(18))
}

위 테스트가 실패한다면 로직의 예외처리가 정상작동하는 것이다.

이렇게 예외 처리 또한 테스트로 확인할 수 있다.

참고로 assertThorws는 리턴으로 예외 객체의 인스턴스를 받을 수 있고, 받은 인스턴스를 통해 예외 메시지를 확인할 수도 있다.
이 부분은 직접 해보면 좋을 것 같다.

실패 메세지 스트링과 람다식

테스트가 실패했을 때 출력하는 메세지를 직접 설정할 수 있다.

대부분 Assertion메서드의 3번째 혹은 마지막 매개변수 자리에 들어간다.
스트링을 바로 넣을 수도 있고, 람다식을 넣을 수도 있다.

예를 보면 다음과 같다.
assertEquals(기대값, 실제값, 메시지);
assertEquals(기대값, 실제값, () -> 메시지)

스트링을 사용하는 것과 람다식을 사용하는 것의 차이가 뭔데?

스트링 연산에서 성능의 차이가 발생한다고 한다.

int num = 3;
assertEquals(5, num, num + "은 기대하는 값이 아니에요!");

위에서 num + "은 기대하는 값이 아니에요!"의 스트링 연산이 있다.
위 처럼 스트링으로 직접 넘기면 연산이 필수적으로 수행된다.
하지만 람다식으로 넘기게 되면 테스트가 실패했을 때만 연산이 수행된다고 한다.

엄청 난 성능차이라고 생각하지는 않지만 차이를 확실히 알고가면 좋을 것 같아서 정리하였다.

시나리오를 가정하여 실제 테스트

다음의 시나리오를 가정한다.

  1. 현재 나는 은행 ATM기 앞에 있다.
  2. 나의 잔고는 50,000원이다.

위 시나리오를 위해 Account(계좌) 클래스를 작성한다.

class Account {
    private String customerName;
    private String accountNumber;
    private Long balance;

    public Account(String customerName, String accountNumber, Long balance) {
        this.customerName = customerName;
        this.accountNumber = accountNumber;
        this.balance = balance;
    }

    public String getCustomerName() {
        return this.customerName;
    }

    public String getAccountNumber() {
        return this.accountNumber;
    }

    public Long getBalance() {
        return this.balance;
    }

    public Long withDraw(Long balance) {
        if(this.balance - balance < 0) {
            throw new RuntimeException("잔고가 부족합니다.");
        } else {
            this.balance -= balance;
            return balance;
        }
    }
}
  1. assertEquals - 나의 계좌인지 테스트

현재 기대값으로 "SolChan"을 주었기 때문에 테스트는 성공한다.
다른 값을 기대값으로 설정하면, 테스트는 실패할 것이고 직접 설정한 실패 메세지가 출력될 것이다.

    @Test
    @DisplayName("계좌 시나리오 - 나의 계좌인가?")
    void accountTest1() {
        Account account = new Account("SolChan", "123-456-789", 50000L);
        assertEquals("SolChan", account.getCustomerName(), () -> account.getCustomerName() + "의 계좌가 아닙니다.");
    }
  1. assertNotNull - 계좌번호가 null이 아닌지 테스트
    계좌를 생성할 때, 계좌번호를 null로 하였다.

assertNotNull은 null은 실제값이 null이 아니기를 기대하기 때문에 테스트는 실패한다.

    @Test
    @DisplayName("계좌 시나리오 - 계좌번호가 null이 아닌가?")
    void accountTest2() {
        Account account = new Account("SolChan", null, 50000L);
        assertNotNull(account.getAccountNumber(), () -> account.getCustomerName() + "의 계좌번호가 없습니다.");
    }
  1. assertTrue - 잔고가 마이너스가 아닌지 테스트
    계좌의 금액을 -로 생성하였다.
    그래서 account.getBalance() >= 0L 연산 결과가 false이다.

결과가 false이기 때문에 테스트는 실패한다.

    @Test
    @DisplayName("계좌 시나리오 - 잔고가 마이너스가 아닌가?")
    void accountTest3() {
        Account account = new Account("SolChan", "123-456-789", -50000L);
        assertTrue(account.getBalance() >= 0L, () -> account.getCustomerName() + "의 잔고가 마이너스입니다.");
    }
  1. assertThrows - 보유 잔고 이상의 출금 요청 테스트
    계좌의 잔고를 50,000원으로 생성하였다.
    보유잔고보다 큰 금액을 인출하려는 경우, RuntimeException을 발생시키도록 Account클래스를 작성하였다.

따라서 아래처럼 80,000원을 인출하려고 하면 RuntimeExceptine이 발생되고 테스트는 성공한다.
위에서 설명했듯이 assertThrows는 인스턴스를 리턴받을 수 있으며, 받은 인스턴스를 통해 발생한 예외의 메세지를 출력하여 확인할 수 있다.

    @Test
    @DisplayName("계좌 시나리오 - 잔고 이상의 출금을 요청하는가?")
    void accountTest4() {
        Account account = new Account("SolChan", "123-456-789", 50000L);
        RuntimeException exception = assertThrows(RuntimeException.class, () -> account.withDraw(80000L));
        System.out.println(exception.getMessage());
    }
    // 출력 메세지: 잔고가 부족합니다.
 

'개발 > Test' 카테고리의 다른 글

[Test] Mockito를 사용해보자 (2)  (0) 2022.02.10
[Test] Mockito를 사용해보자 (1)  (0) 2022.02.10
[Test] JUnit5을 사용해보자 (1)  (0) 2022.02.10