테스트 코드 작성의 중요성은 이제는 널리 알려져 있다.
어떤 코드든 예상치 못한 상황이 발생할 수 있기에, 이를 방지하고자 테스트 코드를 작성한다.
단위 테스트, 기능 테스트 다양한 기준으로 작성되며,
테스트 코드를 먼저 작성한 뒤 구현을 하는 TDD, DDD, BDD 등 다양한 개발 방법론도 존재한다.
테스트 코드 작성을 위해 다양한 도구들이 사용된다.
그중 Java를 테스트하기 이한 JUnit에 대해 간단히 살펴보고,
조금 더 다양하게 테스트하기 위한 Assertion 메서드들을 정리해보려 한다.
JUnit 이란?
JVM 위에서 쉽게 테스트를 할 수 있게 해주는 도구이다.
이번 내용에서 다루는 JUnit5는 Java 8 이상에서 동작함에 유의하자!
Assertion 이란?
테스트를 수행하며, 결과에 따라 테스트 통과 여부를 결정해 준다.
static class로 이루어진 단정 메서드라고 불리며,
예상값과, 실제 수행 로직을 통해 테스트 통과 여부를 결정한다.
테스트 코드 작성하기
테스트 코드를 작성하기 앞서 몇 가지 알고 가자.
- JUnit5부터 접근제어자를 명시할 필요가 없어졌다.
따라서 테스트 클래스와 테스트의 대상이 되는 메서드들의 접근제어자는 명시하지 말자. - 테스트 메서드는 @Test 어노테이션을 기반으로 테스트된다. 테스트를 작성했다면, 어노테이션을 꼭 붙여주자.
인텔리제이 기준 cmd + n 단축키를 통해 빠르게 테스트 메서드를 만들 수 있다. - @DisplayName을 활용해 어떤 테스트인지 명시하자.
이전에는 테스트 메서드명을 한글로 snake 케이스로 작성해 명시해 주었지만 DisplayName 어노테이션을 통해 명시할 수 있게 되었다. 이를 활용해서 어떤 테스트인지 꼭 명시해 주자. - 테스트 코드 내부에도 중복된 부분을 메서드로 뽑아낼 수 있다. 이때 private 접근제어자를 사용해 예상치 못한 상황을 방지하자.
public class People {
private final String name;
private final int age;
public People(String name, int age) {
this.name = name;
if (age < 0) {
throw new IllegalArgumentException("음수 나이는 잘못된 입력입니다.");
}
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public boolean isAdult() {
return age > 19;
}
}
간단한 예시를 위해 다음과 같은 People 클래스를 구현해 주었다.
이름과 나이를 입력받고, 어른인지 아닌지 확인하는 메서드까지 구현된 클래스이다.
위 People 클래스를 테스트해보자.
Test 클래스 생성
@DisplayName("People Test")
class PeopleTest {
}
클래스명에 커서를 올린 뒤, opt + enter 단축키를통해 빠르게 테스트 클래스를 생성할 수 있다.
테스트 클래스를 생성하고, 앞서 말했던 것처럼 @DisplayName 어노테이션을 달아 어떤 테스트 클래스인지 명시해 주자.
assertEquals
@Test
@DisplayName("이름, 나이로 올바르게 생성한다.")
void create() {
People myeongSoo = new People("MyeongSoo", 12);
assertEquals(12, myeongSoo.getAge());
assertEquals("MyeongSoo", myeongSoo.getName());
}
생성자를 테스트하는 코드이다. 12살의 명수라는 객체를 생성한 뒤, 명수의 나이가 12가 맞는지, 명수의 이름이 명수가 맞는지 확인한다.
즉, assertEquals 메서드는 동일한 값을 갖는지 확인하는 객체이다.
위 메서드를 살펴보면 assertEquals(int expected, int actual) 다음과 같은 입력을 갖는다.
즉, 예상되는 결괏값을 먼저 인자로 기입한 뒤, 실제 수행되는 로직을 다음 인자로 기입해주어야 한다.
assertFalse
@Test
@DisplayName("나이를 반환한다.")
void checkAge() {
People myeongSoo = new People("MyeongSoo", 12);
assertFalse(myeongSoo.isAdult());
}
예상되는 결과값을 기입하는 방식도 존재하지만, 메서드명 자체로 결과를 지정한 뒤 테스트 할 수 있다.
boolean 형태를 반환하는 로직의 메서드들을 기입해 테스트를 진행할 수 있다.
이외에도 ArrayEquals, Null, NotNull 등등 다양한 메서드들이 존재한다.
다양한 메서드들과 자세한 사용법이 궁금하다면 공식문서를 참고하는 것이 도움 된다!
Assertions (JUnit 5.9.3 API)
Assert that all supplied executables do not throw exceptions. If any supplied Executable throws an exception (i.e., a Throwable or any subclass thereof), all remaining executables will still be executed, and all exceptions will be aggregated and reported i
junit.org
assertThrows
@Test
@DisplayName("나이에 음수값이 들어갈 경우 예외가 발생한다.")
void createNegativeAge() {
assertThrows(IllegalArgumentException.class,
() -> new People("MyeongSoo", -1));
}
예외가 올바르게 던져졌는지 테스트할 수 있는 메서드도 존재한다.
앞선 People 객체에서 생성자로 음수의 나이를 입력받았을 경우 예외를 던지도록 구현했다.
이 또한 assertThrows를 통해 테스트해볼 수 있다.
순서대로, 발생될 exception, 예외가 던져질 로직을 기입하면 된다.
assertAll
@Test
@DisplayName("이름, 나이로 올바르게 생성한다.")
void create() {
People myeongSoo = new People("MyeongSoo", 12);
assertAll(
() -> assertEquals(12, myeongSoo.getAge()),
() -> assertEquals("MyeongSoo", myeongSoo.getName()),
() -> assertFalse(myeongSoo.isAdult()),
() -> assertThrows(IllegalArgumentException.class,
() -> new People("JunHa", -1)));
}
여러 테스트를 한꺼번에 수행해야 할 때 사용할 수 있는 assertAll 메서드이다.
그냥 여러 개의 assertion을 작성하면 되는데 왜 굳이 all을 사용할까?
물론 모든 테스트가 성공한다면 아무런 문제가 없다.
하지만 4개의 테스트를 진행한다고 하였을 때, 1번 테스트를 실패한다면 2, 3, 4 모든 테스트를 수행하지 않는다.
즉, 1번 테스트에 대한 수정을 한 뒤 테스트 코드를 다시 돌려보면 2, 3, 4번에 테스트가 또 막혀버리는 상황이 발생한다.
하지만 assertAll을 통해 테스트를 진행한다면 1, 2, 3, 4 모든 테스트를 수행하고 결과를 반환하므로 여러 테스트가 묶여 있을 경우에는 assertAll을 통해 테스트하자.
더 자세한 내용은 위에 달아두었던 공식 문서를 참조하면 좋을 것 같다.
나도 아직은 공식 문서를 찾아보기보다 검색을 하는 편인데, 정확한 정보가 존재하는 공식 문서를 찾아보는 습관을 길러야 할 것 같다.
테스트 코드 관련 정보가 있는 JUnit 공식 문서도 참조해서 다양한 테스트 코드를 작성해 보는 것도 좋을 것 같다.
https://junit.org/junit5/docs/current/user-guide/#overview-what-is-junit-5
JUnit 5 User Guide
Although the JUnit Jupiter programming model and extension model do not support JUnit 4 features such as Rules and Runners natively, it is not expected that source code maintainers will need to update all of their existing tests, test extensions, and custo
junit.org
스스로 공부한 내용을 토대로 정리한 글입니다. 오타나 잘못된 내용은 언제나 지적해 주세요!
'Study > Java' 카테고리의 다른 글
[Java] 불변 객체 (Immutable Object) 란? (0) | 2023.07.23 |
---|---|
[Java] 자바의 스레드와 동작 과정 (0) | 2023.07.15 |
[JAVA] 동일성, 동등성 그리고 equals, hashcode 재정의 (0) | 2023.07.09 |
[Java] 코딩테스트에 자주 쓰는 문법 정리 (0) | 2022.09.29 |
[Java / IntelliJ] 인텔리제이 입출력 txt로 받기 (0) | 2022.09.29 |