[Junit] @BeforeEach, @BeforeAll, @AfterEach, @AfterAll에 대해 알아보자

토비의 스프링 스터디를 진행하며 2장에서 더 알아보고 싶은 내용으로 위와 같은 주제를 정했다.
이제부터 자세히 알아보자!

1. 기본적인 소개

  • 픽스쳐(fixture)

    • 테스트를 수행하는 데 필요한 정보나 오브젝트를 픽스처라고한다.
  • @BeforeAll == @BeforeClass

    • 테스트 클래스에 있는 어떤 테스트를 처음 실행하기 전 한 번만 실행된다.
    • static으로 만들어져야 하며 리턴타입은 void이어야 함
  • @BeforeEach == @Before

    • 현재 클래스의 각 @Test, @RepeatedTest, @ParameterizedTest 또는 @TestFactory 메소드보다 먼저 메소드가 실행되어야 함을 의미
    • 비즈니스 로직이 복잡해지고 테스트에 여러 초기화가 필요하다면 여러 개의 @BeforeEach 메소드를 만들 수 있다.
    • 다만 초기화 메소드들 사이의 실행 순서는 보장되지 않으니 순서가 필요한 경우에는 @Order 어노테이션을 사용해 순서를 지정해준다.
  • @AfterAll == @AfterClass

    • 테스트 클래스에 있는 테스트를 모두 실행하고 그 후에 한 번만 실행된다.
    • static으로 만들어져야 하며 리턴타입은 void이어야 함
  • @AfterEach == @After

    • 현재 클래스의 각 @Test, @RepeatedTest, @ParameterizedTest 또는 @TestFactory 메소드 이후에 메소드가 실행되어야 함을 의미
    • 테스트가 실패해도 수행됨
  • 테스트 코드에서 중복된 코드들을 줄이기위해 사용되는 어노테이션!

  • 보통은 테스트 수준의 초기화(@BeforeEach, @AfterEach)면 충분하다!


2. 내부 구현

  • annotationTest.java

  • LifecycleMethodUtils.java

  • TestMethodTestDescriptor.java


3. 실제 오픈소스 사례

  • 스프링 프레임워크 코어

     	@BeforeEach
    	public void setUp() throws Exception {
    		this.webRequest = new ServletWebRequest(new MockHttpServletRequest());
    		this.attributeStore = new DefaultSessionAttributeStore();
    		this.attributeHandler = new SessionAttributesHandler(TestController.class, this.attributeStore);
    		this.controller = new TestController();
    		this.mavContainer = new ModelAndViewContainer();
    	}
  • 라인 아르메리아 (Java 8 및 Netty 상에 비동기 RPC/API 클라이언트-서버를 구현한 것 - 라인 메신져에서 사용됨)

      		@BeforeEach
       		void setUp() {
           		when(eventLoop.inEventLoop()).thenReturn(true);
           		doAnswer((Answer<Void>) invocation -> {
               		invocation.<Runnable>getArgument(0).run();
               		return null;
           		}).when(eventLoop).execute(any());
           		ctx = ServiceRequestContext.builder(HttpRequest.of(HttpMethod.GET, "/"))
                                      		.eventLoop(eventLoop)
                                      		.build();
       		}
  • 네이버 spring-jdbc-plus

     	@BeforeEach
    	public void setUp() {
    		this.context.setForceQuote(false);
    		this.sqlGenerator = createSqlGenerator(DummyEntity.class);
    	}

4. 잘 활용해보기

  • @BeforeEach를 사용해 전역으로 사용하는 객체초기화하는 경우의 문제점

    1) 어떤 상황일 경우에 이 기대값이 나오는지를 한 눈에 알 수가 없다.
    • setup() 메소드를 다 확인해보아야 한다
    • 객체가 어떤 값을 가지고 있는지
    • 어떤 함수가 어떻게 mock/stub 처리 되었는지
    2) 해당 테스트 클래스에서 모든 테스트가 결합되어 버린다.
    • @BeforeEach 하나만 변경해도 모든 메소드가 영향을 받는다.
  • 해결방법

    • 클래스 내부에 private 팩토리 메소드를 만들어 사용
    • 클래스 외부에 static 팩토리 메소드를 만들어서 사용
  • 얻을 수 있는 장점

    • 전체 테스트 코드의 양이 줄어들고, 재사용성이 좋다
    • 각각의 테스트 메소드의 가독성이 좋아지고, 맥락 파악이 쉬워진다.
    • 각각의 테스트 픽스처가 모두 1회성으로 끝나는 지역변수를 사용하기 때문에 테스트간 결합도가 낮아진다

추가로 알아볼 점) @BeforeAll이 @BeforeEach보다 더 메모리를 많이 먹는다?


참고링크

[자바와 JUnit을 활용한 실용주의 단위 테스트]
https://junit.org/junit5/docs/current/user-guide/
https://jojoldu.tistory.com/611
https://github.com/spring-projects/spring-framework/blob/main/spring-web/src/test/java/org/springframework/web/method/annotation/ModelFactoryTests.java
https://github.com/line/armeria/blob/master/core/src/test/java/com/linecorp/armeria/client/BlockingWebClientTest.java
https://github.com/naver/spring-jdbc-plus/blob/master/spring-data-jdbc-plus-sql/src/test/java/com/navercorp/spring/data/jdbc/plus/sql/convert/SqlGeneratorEmbeddedTest.java

좋은 웹페이지 즐겨찾기