데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      JUnit5에 병렬화를 도입한 이야기 - 클래스 단위

      kevin0928 25.02.25
      2,149 9 3
      DEVOTEE 요약
      YongGoose는 여러 오픈소스 프로젝트에 기여해왔으며, 이번 글에서는 JUnit5 Vintage 엔진에 병렬화 기능을 도입한 과정을 설명합니다. JUnit5의 Jupiter 엔진과 달리, Vintage 엔진에는 기본적으로 테스트 병렬 실행 기능이 없습니다. YongGoose는 Vintage 엔진의 테스트를 병렬로 실행하도록 수정했고, 이는 JUnit5 5.12 버전부터 사용할 수 있습니다.
      DEVOTEE 추천 블로그

      안녕하세요, Junit-team/junit5, spring/spring-boot, apache/seata, naver/fixture-monkey 등 여러 오픈소스 프로젝트에 기여한 YongGoose입니다.

      찾아보니 JUnit5에는 전 세계에서 35번째로 많이 기여를 했더라고요.

      Commit 순이 아닌, Additions 순으로 하면 17번째입니다.🙂

      이번 글에서는 제가 JUnit5 Vintage엔진에 병렬화를 도입한 이야기를 해보려고 합니다.

      잠깐만 JUnit Vintage Engine이 뭐야..?

       

      우선, 간단히 JUnit5의 구조 및 Vintage Engine에 대해 설명을 하겠습니다.


      출처 : https://www.swtestacademy.com/junit-5-architecture/


      JUnit5은 테스트 프레임워크를 JVM에서 실행하기 위해 JUnit Platform을 제공합니다.

      Platform은 주로 다음의 기능을 수행합니다.

      • TestEngine API 정의 : 플랫폼에서 실행될 테스트 프레임워크를 개발할 수 있는 API를 제공합니다.

      • Console Launcher 제공 : 명령줄에서 플랫폼을 실행할 수 있게 해줍니다.

      • IDE 및 빌드 도구 지원 : Intellij, Eclipse, VSCode 등에서 테스트를 실행할 수 있게 지원합니다.

      TestEngine은 테스트를 발견하고 실행하는 핵심 인터페이스입니다.

      • 테스트 발견(Discovery) : Launcher, IDE가 테스트 대상을 인식할 수 있게 도와줍니다.

      • 테스트 실행(Execution) : 발견된 테스트들을 실행할 수 있게 도와줍니다. (시작, 완료, 성공, 실패등의 이벤트를 발생시킵니다)

      JUnit Jupiter는 JUnit5의 새로운 프로그래밍 및 확장 모델을 제공합니다.

      • Jupiter TestEngine을 통해 최신 방식으로 테스트를 작성하고 확장할 수 있게 도와줍니다.

      JUnit Vintage는 기존의 JUnit 3, JUnit 4 기반 테스트를 JUnit 5 플랫폼에서 실행할 수 있게 해줍니다.

       

      JUnit user-guide에서 더욱 자세히 볼 수 있습니다.

      JUnit 5 User Guide


      그래서 어떤 기여를 했는데?

      배경에 대해 자세히 설명하자면, JUnit Jupiter-Engine에는 테스트를 병렬로 실행시킬 수 있는 기능이 있습니다.

      JUnit 5 User Guide

       

      하지만 Vintage-Engine에는 테스트를 병렬로 실행시킬 수 있는 기능이 없습니다.

      그래서 모든 테스트는 순차적으로 실행이 됩니다.

      아래의 이슈에서 자세히 볼 수 있습니다.

      Issue: https://github.com/junit-team/junit5/issues/2229

       

      아래의 시퀀스 다이어그램처럼, for 반복문을 사용하여 테스트 요소를 순차적으로 실행하고, 처리한 요소를 제거하는 방식으로 진행됩니다.


      아래의 사진과 같이 JUnit Vintage 엔진에서도 병렬화를 지원해 주라는 요구가 많아 제가 작업을 하게 되었습니다.

      지피티 사진 잘 만드네요.....

      우선 병렬화를 도입하기 위해 어디에 도입을 하면 좋을지에 대해 생각을 해보았습니다.


      이때, 이슈에서 JUnit5의 메인테이너인 Marc가 RunnerExecutor를 병렬로 호출하라는 힌트를 주는 메시지를 확인했습니다.

      정확히는 아래의 로직에 병렬화를 적용하는 것입니다.


      병렬화를 위해 스레드 풀을 생성하여 여러 작업을 동시에 실행하는 로직을 구현하기로 결정했습니다.

      초기에는 테스트라는 큰 단위를 여러 개의 작은 작업으로 분할하여 실행함으로써, ForkJoinPool이 제공하는 work-stealing 기능을 활용해 성능 향상을 기대하며 이를 적용했습니다.

      그러나 메인테이너님께서는 ForkJoinPool 사용이 현재 구조에서는 별다른 이점을 제공하지 않는다는 의견을 주셨습니다.


      그래서 왜 ForkJoinPool을 사용하는 것이 이점이 없을까..?라는 고민을 해보았습니다.

      우선, ForkJoinPool은 하나의 Task가 여러 개의 SubTask로 나뉘는 작업일 때 효율적입니다.

      여러 개의 SubTask로 나눠야 다른 스레드가 더 이상 처리할 작업이 없을 때 작업을 가져올 수 있기 때문입니다.

      출처 : Infoworld


      처음에는 테스트(클래스)가 여러 작업(메서드)으로 나누어져 병렬 실행될 것으로 기대하며 작업을 진행했습니다.

      하지만 자세히 살펴보니, 실제로는 각 테스트(클래스)가 하나의 독립된 작업으로 구성되어 있다는 것을 알게 되었습니다.

      이런 이유로 메인테이너님께서는 ForkJoinPool을 사용하는 것에는 별다른 이점이 없다고 말씀하셨던 것입니다.

      결과적으로 고정된 크기를 가지는 newFixedThreadPool을 통해 스레드 풀을 생성했습니다.


      하지만 하나의 테스트를 전체적으로 실행하는 것보다 각 메서드를 개별적으로 실행하는 것이 성능 면에서 분명히 이점이 있을 것이라고 생각했습니다.

      그래서 메인테이너님께 코멘트를 통해 제안을 했습니다.


      코멘트의 길이가 길어 한 페이지에 모두 담기지가 않아 링크를 별도로 남깁니다.

      Support parallelization in junit-vintage-engine by YongGoose · Pull Request #4135 · junit-team/junit5

      제안을 한 덕분에 저는 별도의 PR을 통해 메서드 단위 병렬화를 진행할 수 있었습니다.

      다음 글에서 자세히 소개하겠습니다.

      아래의 코드는 비동기를 통해 병렬화를 적용한 부분입니다.

      private boolean executeInParallel(VintageEngineDescriptor engineDescriptor,
              EngineExecutionListener engineExecutionListener, ExecutionRequest request) {
          ExecutorService executorService = Executors.newFixedThreadPool(getThreadPoolSize(request));
          RunnerExecutor runnerExecutor = new RunnerExecutor(engineExecutionListener);
      
          List<CompletableFuture<Void>> futures = new ArrayList<>();
          for (Iterator<TestDescriptor> iterator = engineDescriptor.getModifiableChildren().iterator(); iterator.hasNext();) {
              TestDescriptor descriptor = iterator.next();
              CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                  runnerExecutor.execute((RunnerTestDescriptor) descriptor);
              }, executorService);
      
              futures.add(future);
              iterator.remove();
          }
      
          CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture<?>[0]));
          boolean wasInterrupted = false;
          try {
              allOf.get();
          }
          catch (InterruptedException e) {
              logger.warn(e, () -> "Interruption while waiting for parallel test execution to finish");
              wasInterrupted = true;
          }
          catch (ExecutionException e) {
              throw ExceptionUtils.throwAsUncheckedException(e.getCause());
          }
          finally {
              shutdownExecutorService(executorService);
          }
          return wasInterrupted;
      }

      전체적인 설명은 아래의 이미지에서 볼 수 있습니다.

      포트폴리오에서 발췌한 이미지입니다.



      전체적인 코드 및 제가 메인테이너님과 나눈 대화를 볼 수 있는 Pull Request입니다.

      Pull Request: https://github.com/junit-team/junit5/pull/4135


      그래서 지금 사용할 수 있어?

      어제, JUnit5 5.12 버전이 배포되었습니다.

      5.12 버전에서 제가 생성한 기능(JUnit-vintage 클래스/메서드 단위 병렬화)을 사용할 수 있습니다.

      JUnit-vintage 병렬화 기능을 사용하고 싶은데 질문이 있으신 분은 깃허브에 있는 이메일로 연락 주시면 감사하겠습니다.


      또한 커피챗, 오픈소스 관련 얘기도 환영합니다. 🙂


      https://junit.org/junit5/docs/5.12.0/release-notes/

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      kevin0928 님의 최신 블로그

      더보기

      DEVOTEE 추천 블로그

      동영상 기고하기