티스토리 뷰

Java & Kotlin

[Java] JUnit의 진화 과정과 public 접근 제어자

망나니개발자 2023. 3. 7. 10:00
반응형

아래의 내용은 토비님과 토비의 스프링 읽기를 하면서 얘기가 나온 부분을 개인적으로 공부한 후에 정리한 것입니다.

 

 

 

1. JUnit의 진화 과정과 public 접근 제어자


Java 진영에서 사용되는 테스트 프레임워크로는 JUnit이 있다. 오늘날 우리에게 익숙한 JUnit4와 5는 각각 2006년과 2016년에 출시되었다. 그리고 그 이전 버전들도 있는데, 이번에는 JUnit3부터 살펴보고자 한다.

  • Junit3: 2007년 마지막 Release
  • Junit4: 2006년 첫 Release
  • Junit5: 2016년 첫 Release

 

 

 

[ Junit3의 규칙과 관례 ]

JUnit3를 사용해 테스트를 실행하기 위해서는 몇 가지 규칙 및 관례가 있는데, 크게 다음과 같다.

  • 테스트 클래스는 TestCase 클래스를 상속받아야 함
  • 테스트 메소드는 반드시 test로 시작해야 함
  • 테스트 클래스와 메소드는 반드시 public이여야 함

 

 

실제로 JUni3 기반의 테스트를 작성해보면 다음과 같이 작성이 된다.

import junit.framework.TestCase;

public class CalculatorTest extends TestCase {

    public void testAdd() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

}

 

 

만약 TestCase 클래스를 상속하지 않으면, JUnit3는 해당 클래스를 테스트 클래스로 인식하지 않는다. 또한 테스트 메소드가 test로 시작하지 않으면 테스트 메소드로 인식하지 않는다. 그리고 테스트 클래스와 테스트 메소드의 가시성은 반드시 public 이여야 하는데, 이와 관련해서는 JUnit 개발자들이 밝힌 이유가 있다.

JUnit은 기본적으로 테스트 클래스의 메소드를 리플렉션 API를 사용해 호출한다. 리플렉션을 사용하는 이유는 Pluggable Selector 패턴을 이용했기 때문이다. 이 부분에 대해 자세히 살펴보기 위해 위의 테스트를 다음과 같이 수정해보도록 하자. 생성자의 호출 횟수를 파악하기 위해 생성자에 print 문을 추가하였고, 동일한 테스트를 3개로 만들었다.

import junit.framework.TestCase;

public class CalculatorTest extends TestCase {

    public CalculatorTest() {
        System.out.println("CalculatorTest created");
    }

    public void testAdd() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

    public void testAdd2() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

    public void testAdd3() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

}

 

 

위의 테스트를 실행하면 호출 결과는 다음과 같다. 테스트 클래스의 객체가 3번 만들어지는 것이다.

CalculatorTest created
CalculatorTest created
CalculatorTest created

 

 

이러한 이유는 JUnit이 독립적인 테스트를 보장하기 위함이다. 그래서 각 테스트 별로 새로운 테스트 클래스 객체를 생성한다. 그런데 이렇게 테스트의 독립성을 보장하려다 보니 문제가 생겼는데, 어느 테스트 클래스 객체에서 어느 테스트 메소드를 호출해줘야 할지 선별하기 어렵기 때문이다. 그래서 JUnit은 Pluggable Selector 패턴을 적용해 동적으로 특정 테스트 클래스 객체에서는 특정 메소드를 호출하도록 선별해주어야 했고, 그 과정에서 리플렉션을 사용하면서 test로 시작하는 메소드만 테스트로 인식하도록 하였다.

그런데 이러한 JUnit이 처음 만들어 졌을때 사용되었던 JDK 버전이 1.1인데, JDK 1.1에서는 리플렉션에서 public 메소드만 접근을 허용했다. 따라서 과거에 JUnit의 테스트 메소드는 모두 public이여야 했고, 메소드 이름은 test로 시작해야만 했다.

참고로 과거에는 Test라는 인터페이스를 구현해서 테스트 케이스를 만들어야 했고, 그래서 테스트 클래스 하나당 테스트 메소드가 1개 밖에 작성할 수 없었던 시절도 있었다고 한다.

 

 

 

[ Junit4의 규칙과 관례 ]

JUnit4에서도 테스트를 실행하기 위해서는 몇 가지 규칙 및 관례가 있는데, 크게 다음과 같다.

  • 테스트 클래스와 메소드는 반드시 public이여야 함
  • 테스트 메소드는 @Test 어노테이션이 붙어있어야 함

 

 

실제로 JUni4 기반의 테스트를 작성해보면 다음과 같이 작성이 된다.

public class CalculatorTest {

    @Test
    public void addTest() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

}

 

 

JDK 1.5에 애노테이션이 들어오게 되면서 JUnit에도 큰 변화가 하나 생겼는데, 바로 @Test 어노테이션을 활용하는 것이다. JUnit4도 마찬가지로 독립적인 테스트를 보장하기 위해 각각의 테스트 클래스 객체를 생성하는데, 테스트 메소드를 선별하는 과정에서 @Test가 붙은 메소드 만을 테스트로 인식하면 되었다. 그래서 테스트 메소드가 test로 시작할 필요가 없어져 이러한 부분이 개선되었다.

하지만 여전히 테스트 클래스와 메소드는 반드시 public 이여야 한다. JDK1.2부터는 리플렉션 API을 통해 모든 접근레벨을 다 허용하기 시작했다. 그럼에도 불구하고 JUni4까지는 테스트 클래스와 메소드에 public을 붙여주어야 하는데, JUnit 개발자들이 밝히기를 기존의 전통을 유지하기 위함이였다고 한다.

 

 

 

 

[ Junit5의 규칙과 관례 ]

JUnit5에서 테스트를 실행하기 위해서는 다음의 한 가지만 지켜주면 된다.

  • 테스트 메소드는 @Test 어노테이션이 붙어있어야 함

 

 

실제로 JUni5 기반의 테스트를 작성해보면 다음과 같이 작성이 된다.

class CalculatorTest {

    @Test
    void addTest() {
        // given
        Calculator calculator = new Calculator();

        // when
        int result = calculator.add(1, 2);

        // then
        assertEquals(result, 3);
    }

}

 

 

JUnit5 부터는 public으로 만드는 전통을 풀어버렸다. 그래서 private 외의 접근제어자는 모두 사용할 수 있고, 일반적으로 default 접근자를 통해 손쉽게 타이핑할 수 있게 되었다. 그 외에 독립적인 테스트를 위해 테스트 클래스 객체를 새롭게 만드는 등은 모두 동일하다. 사실 JUnit에서는 테스트 성능을 최적화할 수 있도록 1개의 테스트 클래스 객체만을 사용하는 방법을 제공하는데, 이와 관련해서는 @TestInstance 관련해서를 찾아보면 된다.

JUnit5에는 사실 기존의 JUnit4와 달리 인터페이스 모듈과 실제 구현 모듈이 분리되는 등 많은 변화가 있었다. 이와 관련해서는 다음에 시간이 될 때 자세히 다룰 예정이다.

 

 

 

 

 

 

아래의 내용은 토비님과 토비의 스프링 읽기를 하면서 얘기가 나온 부분을 개인적으로 공부한 후에 정리한 것입니다. 관련된 내용은 아래의 링크에도 나와있으니 확인해보시면 좋을 것 같습니다.

 

 

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함