JUnit in Action : 2. JUnit 핵심 들여다보기

    들어가기 전

    이 글은 JUnit in Action 2장을 공부하며 정리한 글입니다. 


    2. JUnit 핵심 들여다보기

    프로그램은 매일 커져간다. 매번 새로운 클래스가 추가될텐데, 여러 클래스 사이의 복잡한 상호 작용을 모두 예측하는 것은 불가능하다. 따라서 어떤 부분이 변경되었는지와 관련없이, 원할 때 언제든 테스트 전체를 돌려보고 클래스 사이의 예상치 못한 상호 작용을 파악할 수 있어야 한다. 


    2.1 Junit의 핵심

    JUnit의 가장 기본적인 구성요소는 테스트 클래스와 테스트 메서드다. 개발자는 테스트 클래스와 테스트 메서드를 작성하는데 집중하면 된다. JUnit의 테스트 클래스, 테스트 메서드를 생성할 때는 다음 조건을 반드시 만족해야한다.

    • 테스트 클래스
      • 반드시 public 클래스여야한다.
      • 기본 생성자가 반드시 존재해야한다.
    • 테스트 메서드
      • @Test 어노테이션을 가져야 한다.
      • public 이어야 한다.
      • 파라미터를 받으면 안된다.
      • 반환 타입은 void 여야한다. 

    예를 들면 아래와 같이 코드를 작성할 수 있다.

    public class CalculatorTest {
        @Test
        public void testAdd() {
            Calculator calculator = new Calculator();
            double result = calculator.add(10, 50);
            Assertions.assertEquals(60, result, 0);
        }
    }

    JUnit은 @Test 어노테이션이 있는 메서드를 호출할 때 마다, 해당 테스트 클래스의 인스턴스를 하나씩 생성한다. 각 테스트 메서드들은 독립된 메모리 공간에서 실행시켜서 각 단위 테스트를 격리시킨다. 모든 테스트 메서드는 각기 다른 테스트 클래스 인스턴스에서 실행되므로, 인스턴스 변수는 공유되지 않는다. 

    예를 들어 CalculatorTest 클래스에 testAdd(), testSubstrate() 테스트 메서드가 있다고 가정해보자. 이 때 testAdd(), testSubstrate() 수행을 위해 CalculatorTest 인스턴스가 2개 생성되고 테스트가 수행된다. 

     

    자주 사용하는 assert 메서드

    assert 메서드 사용 목적
    assertArrayEqauls("message", A, B) 배열 A와 B가 일치함을 확인한다.
    assertEquals("message", A, B) 객체 A와 B가 일치함을 확인한다.
    A.equals(B) 를 수행한다.
    assertSame("message", A, B) 객체 A, B가 같은 객체임을 확인한다.
    assertEquals는 equals()로 검사한다.
    assertSame은 ==으로 검사한다. (하나의 객체인지를 검사)
    assertTrue("message", A) A가 참인지 확인한다.
    assertNotNull("message", A) A가 null이 아님을 확인한다. 

    자주 사용하는 assert 메서드는 위에서 확인할 수 있다. 위의 메서드는 일반적으로 같은 형식으로 작성되어 있기 때문에 이 부분을 잘 숙지하고 사용하면 된다. 같은 형식이라는 것은 다음을 의미한다.

    • A : 기대값
    • B : 실제값 

     

    JUnit의 핵심 객체

    JUnit에는 몇 가지 핵심 객체가 존재한다. 사실 개발자가 주로 만지는 객체는 Assert, 테스트 클래스, 테스트 메서드 객체 정도만 존재한다. 그렇지만 JUnit의 핵심 객체가 무엇인지를 알고 있는 것은 의미가 있는 일이다. 

    JUnit 개념 역할
    Assert 테스트 하려는 조건을 명시한다.
    assert 메서드는 조건을 만족시키면 아무 일도 없음.
    조건을 만족시키지 못하면 예외를 발생시킨다. 
    Test 메서드 @Test 어노테이션을 가진 메서드. 
    JUnit은 테스트 메서드를 실행할 때 마다 테스트 클래스를 생성한다.
    JUnit은 테스트 클래스를 생성하고, 테스트 메서드를 실행한다.
    Test 클래스 @Test 메서드를 포함한 클래스다.
    Suite 여러 테스트 클래스를 하나로 묶는 객체다.
    개발자가 지정하지 않으면 기본적인 스위트가 제공된다. (만들지 않아도 됨) 
    Runner 러너는 테스트를 실행시킨다.
    러너마다 다른 형태의 테스트를 수행한다.

    개인적으로는 Assert, Test 메서드, Test 클래스 정도만 알고 있어도 문제 없다고 생각한다. 


    2.2 파라미터화 테스트 실행하기

    파라미터화(parameterized) 테스트 러너는 하나의 테스트를 여러 번 반복 실행하는 기능을 제공한다. 어떤 내용인지는 아래 코드를 보면 된다. 

    @RunWith(value = Parameterized.class)
    public class ParameterizedTest {
    
        private double expected;
        private double valueOne;
        private double valueTwo;
    
    
        @Parameterized.Parameters
        public static Collection<double[]> getTestParameters() {
            return Arrays.asList(new double[][]{
                            {2, 1, 1}, // 예상 값, 값1, 값2
                            {3, 2, 1}, // 예상 값, 값1, 값2
                            {4, 3, 1}  // 예상 값, 값1, 값2
            });
        }
    
        public ParameterizedTest(double expected,
                                 double valueOne,
                                 double valueTwo) {
            this.expected = expected;
            this.valueOne = valueOne;
            this.valueTwo = valueTwo;
        }
    
        @Test
        public void sum() {
            Calculator calc = new Calculator();
            Assertions.assertEquals(expected, calc.add(valueOne, valueTwo));
        }
    }

    위 코드는 다음과 같이 동작한다. 

    1. JUnit은 getTestParameters()를 호출해 컬렉션 객체를 얻는다. 
    2. 컬렉션에 저장된 배열의 수만큼 순환한다. 
      1. JUnit은 유일한 public 생성자를 찾는다. 이 때 만약 생성자가 2개 이상이라면 AssertionError를 던진다. 생성자에 배열의 원소를 파라미터로 넣어 호출한다. 
      2. 테스트 메서드를 호출해서 테스트 한다. 

    하나의 테스트를 수행할 때, 다양한 파라메터에 대해서 여러 번 동일한 테스트를 수행하는 기능을 제공하는 테스트를 Parameterized 테스트라고 한다. 이 테스트를 하기 위해서는 다음과 같이 작성해야한다. 

    1. 테스트에서 사용할 파라메터를 클래스 변수로 선언한다.
    2. @Parameters라 표시된 메서드를 하나 생성한다.
      • 이 메서드의 시그니처는 반드시 @Parameters public static java.util.Collection이어야 한다.
      • 어떠한 파라메터도 입력 받아서도 안된다. 
      • Collection의 원소는 배열(array)이고, 길이는 모두 같아야 한다. 이 길이는 public 생성자가 받는 파라미터의 수와도 일치해야 한다.  

    2.3. JUnit 테스트 러너

    JUnit의 테스트 러너는 테스트를 실행하는 실행 엔진의 역할을 한다. JUnit4는 이런 테스트 러너를 제공해준다. 

    Runner 용도
    org.junit.internal.runners.JUnit38ClassRunner JUnit 하위 버전과 호환 목적으로 제공 되는 러너. 
    JUnit 3.8 테스트 케이스 전용이다.
    org.junit.runners.JUnit4 JUnit 4 스타일의 테스트 케이스를 실행한다.
    org.junit.runners.Parameterized 같은 테스트 케이스를 다른 입력 값을 사용해 반복 수행한다.
    org.juni.runners.Suite 복수의 테스트를 묶을 수 있는 집합이다.
    테스트 클래스 내의 모든 @Test 메서드를 찾아 실행하는 러너이기도 하다.

    특정 테스트 러너를 사용하고자 한다면 @RunWith() 어노테이션을 클래스에 지정해주면 된다. 다음과 같이 사용할 수 있다.

    @RunWith(value = Parameterized.class)
    public class ParameterizedTest {}

    특정 테스트 러너를 사용하도록 지정해주지 않았다면, 기본 테스트 러너로 테스트가 실행된다. 

     


    2.4 스위트를 이용한 테스트 조직하기

    스위트는 하나 이상의 테스트 케이스를 실행하도록 설계된 녀석이다. 개발자가 따로 지정하지 않으면 테스트 러너가 기본 스위트를 하나 만들어서 테스트를 실행한다. 기본 스위트는 테스트 클래스들을 살펴 @Test가 부여된 모든 메서드를 찾아낸다. @Test 메서드별로 하나씩 테스트 클래스의 인스턴스를 생성하는 일이 이루어진다. 만약 스위트를 직접 만들고 싶다면 아래와 같이 선언하면 된다.

    @RunWith(value = org.junit.runners.Suite.class)
    @Suite.SuiteClasses(value = {
            CalculatorTest.class,MultipleTest.class})
    public class SuiteExampleTest {
        ...
    }

    JUnit 스위트를 지정할 때는 다음과 같이 구현했다.

    • @RunWith를 이용해서 적당한 테스트 러너를 선택.
    • @SuiteClasses를 이용해서, 스위트에서 함께 실행되어야 하는 테스트 클래스들을 명시.

    그런데 maven이나 gradle을 이용하면 JUnit 스위트를 만들 필요없이 한번에 여러 테스트 클래스들을 테스트 해준다. 따라서 JUnit에는 스위트가 있다는 것 정도로만 이해하고 넘어가도 무방할 것 같다. 

    댓글

    Designed by JB FACTORY