programing

JUNIT-Spring @Asyncvoid 서비스 방법

stoneblock 2023. 9. 6. 21:45

JUNIT-Spring @Asyncvoid 서비스 방법

봄맞이 서비스가 있습니다.

@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.
}

문제는 다음과 같습니다.

  • ~하듯이SomeService.asyncMethod(..)주석이 달렸습니다.@Async그리고.
  • 처럼SpringJUnit4ClassRunner을 고수하는@Async의미론

testAsyncMethod실이 통화를 포크할 것입니다.someService.asyncMethod(testData)자체 워커 스레드에 입력한 다음 직접 실행을 계속합니다.verifyResults(), 이전 작업자 스레드가 작업을 완료하기 전에 가능합니다.

어떻게 하면 기다릴 수 있을까요?someService.asyncMethod(testData)결과를 확인하기 전에 완료할 수 있습니까?스프링 4 및 주석을 사용하여 비동기 동작을 확인하는 단위 테스트를 작성하는 방법에 대한 솔루션을 참조하십시오.여기에 해당하지 않습니다.someService.asyncMethod(testData)돌아온다void, 한푼도Future<?>.

위해서@Async의미론을 고수할 이고, 일부 활성 클래스는 주석을 가질 입니다. 예를 들어.

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

  //

}

문제를 해결하기 위해 새로운 Spring 프로필을 소개했습니다.non-async.

만약에non-async프로파일이 활성화되어 있지 않습니다.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.
}

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();

훨씬 더 유능한 것은 비동기 주장을 처리하는 방법이 많은 훌륭한 라이브러리 웨이틸리티입니다.예:

someAsyncCall();
await().atMost(5, SECONDS)
  .untilAsserted(() -> assertThat(userRepo.size()).isEqualTo(1));

ThreadPoolTask를 주입했습니다.실행자

그리고 나서.

실행자.getThreadPoolExecutor().waitTermination(1, TimeUnit).세컨즈);

결과를 확인하기 전에 다음과 같이 합니다.

  @Autowired
  private ThreadPoolTaskExecutor executor;

    @Test
    public void testAsyncMethod() {

        Foo testData = prepareTestData();

        someService.asyncMethod(testData);

        executor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);

        verifyResults();
    }

메소드가 반환되는 경우CompletableFuture사용하다joinmethod - documentation Complete미래:: 가입.

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

단지 @bastiat에 의한 답을 확장하기 위해, 내 생각에 올바른 것으로 여겨져야 할 것이고, 당신은 또한 다음을 명시해야 합니다.TaskExecutor, 여러 실행자와 함께 작업하는 경우.그래서 당신이 기다리고 싶은 정확한 주사를 맞아야 합니다.다음과 같은 구성 클래스가 있다고 가정해 보겠습니다.

@Configuration
@EnableAsync
public class AsyncConfiguration {

    @Bean("myTaskExecutor")
    public TaskExecutor myTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(15);
        executor.setCoreCapacity(10);
        executor.setQueueCapacity(Integer.MAX_VALUE);
        executor.setThreadNamePrefix("MyTaskExecutor-");
        executor.initialize();
        return executor;
    }

    // Everything else

}

그러면 다음과 같은 서비스가 제공됩니다.

@Service
public class SomeServiceImplementation {

    @Async("myTaskExecutor")
    public void asyncMethod() {
         // Do something
    }

    // Everything else

}

@bastiat 답변을 확장하면 테스트는 다음과 같습니다.

@Autowired
private SomeService someService;

@Autowired
private ThreadPoolTaskExecutor myTaskExecutor;

@Test
public void testAsyncMethod() {

    Foo testData = prepareTestData();

    this.someService.asyncMethod(testData);

    this.myTaskExecutor.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);

    this.verifyResults();

    // Everything else
}

그리고 질문과 상관없는 사소한 추천이 있습니다.는 추가하지 @Transactional서비스에 대한 주석, DAO/리포지토리에만 주석을 달 수 있습니다.원자성이어야 하는 특정 서비스 방법에 추가할 필요가 없는 경우.

위의 솔루션에 추가하면 됩니다.

 @Autowired
  private ThreadPoolTaskExecutor pool;

    @Test
    public void testAsyncMethod() {
        // call async method
        someService.asyncMethod(testData);

        boolean awaitTermination = pool.getThreadPoolExecutor().awaitTermination(1, TimeUnit.SECONDS);
        assertThat(awaitTermination).isFalse();

        // verify results
    }

언급URL : https://stackoverflow.com/questions/42438862/junit-testing-a-spring-async-void-service-method