복붙노트

[SPRING] JUnit- Spring @Async void 서비스 메소드 테스트

SPRING

JUnit- Spring @Async void 서비스 메소드 테스트

나는 스프링 서비스를 가지고있다 :

@Service
@Transactional
public class SomeService {

    @Async
    public void asyncMethod(Foo foo) {
        // processing takes significant time
    }
}

그리고이 SomeService에 대한 통합 테스트가 있습니다.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
@Transactional
public class SomeServiceIntTest {

    @Inject
    private SomeService someService;

        @Test
        public void testAsyncMethod() {

            Foo testData = prepareTestData();

            someService.asyncMethod(testData);

            verifyResults();
        }

        // verifyResult() with assertions, etc.
}

문제는 다음과 같습니다.

testAsyncMethod 스레드는 someService.asyncMethod (testData) 호출을 자체 작업자 스레드로 분기 한 다음 이전 작업자 스레드가 작업을 완료하기 전에 직접 verifyResults ()를 계속 실행합니다.

어떻게 결과를 확인하기 전에 someService.asyncMethod (testData)의 완료를 기다릴 수 있습니까? Spring 4 및 주석을 사용하여 비동기 동작을 확인하기위한 유닛 테스트를 작성하려면 어떻게해야합니까? someService.asyncMethod (testData)가 Future 가 아닌 void를 반환하기 때문에 여기에 적용하지 마십시오.

해결법

  1. ==============================

    1.@Async 의미를 준수하기 위해 일부 활성 @Configuration 클래스에는 @EnableAsync 주석이 있습니다 (예 :

    @Async 의미를 준수하기 위해 일부 활성 @Configuration 클래스에는 @EnableAsync 주석이 있습니다 (예 :

    @Configuration
    @EnableAsync
    @EnableScheduling
    public class AsyncConfiguration implements AsyncConfigurer {
    
      //
    
    }
    

    문제점을 해결하기 위해 비동기가 아닌 새로운 Spring 프로파일을 소개했습니다.

    비동기 프로파일이 활성화되어 있지 않으면 AsyncConfiguration이 사용됩니다.

    @Configuration
    @EnableAsync
    @EnableScheduling
    @Profile("!non-async")
    public class AsyncConfiguration implements AsyncConfigurer {
    
      // this configuration will be active as long as profile "non-async" is not (!) active
    
    }
    

    비동기 프로필이 활성화되어 있으면 NonAsyncConfiguration이 사용됩니다.

    @Configuration
    // notice the missing @EnableAsync annotation
    @EnableScheduling
    @Profile("non-async")
    public class NonAsyncConfiguration {
    
      // this configuration will be active as long as profile "non-async" is active
    
    }
    

    이제 문제가되는 JUnit 테스트 클래스에서 비동기 동작을 상호 배제하기 위해 "비동기"프로파일을 명시 적으로 활성화합니다.

    @RunWith(SpringJUnit4ClassRunner.class)
    @SpringApplicationConfiguration(classes = Application.class)
    @WebAppConfiguration
    @IntegrationTest
    @Transactional
    @ActiveProfiles(profiles = "non-async")
    public class SomeServiceIntTest {
    
        @Inject
        private SomeService someService;
    
            @Test
            public void testAsyncMethod() {
    
                Foo testData = prepareTestData();
    
                someService.asyncMethod(testData);
    
                verifyResults();
            }
    
            // verifyResult() with assertions, etc.
    }
    
  2. ==============================

    2.Mockito (직접 또는 Spring 테스트 지원 @MockBean을 통해)를 사용하는 경우,이 경우에 대해 시간 초과가있는 확인 모드가 있습니다. https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#22

    Mockito (직접 또는 Spring 테스트 지원 @MockBean을 통해)를 사용하는 경우,이 경우에 대해 시간 초과가있는 확인 모드가 있습니다. https://static.javadoc.io/org.mockito/mockito-core/2.10.0/org/mockito/Mockito.html#22

    someAsyncCall();
    verify(mock, timeout(100)).someMethod();
    

    당신은 또한 Awaitility를 사용할 수 있습니다 (인터넷에서 찾았고 시도하지 않았습니다). https://blog.jayway.com/2014/04/23/java-8-and-assertj-support-in-awaitility-1-6-0/

    someAsyncCall();
    await().until( () -> assertThat(userRepo.size()).isEqualTo(1) );
    
  3. ==============================

    3.메소드가 CompletableFuture를 리턴 할 경우 Join 메소드 - documentation CompletableFuture :: join을 사용하십시오.

    메소드가 CompletableFuture를 리턴 할 경우 Join 메소드 - documentation CompletableFuture :: join을 사용하십시오.

    이 메서드는 비동기 메서드가 완료 될 때까지 기다렸다가 결과를 반환합니다. 발생한 모든 예외는 주 스레드에서 다시 발생합니다.

  4. from https://stackoverflow.com/questions/42438862/junit-testing-a-spring-async-void-service-method by cc-by-sa and MIT license