좋아, 이건 제목에서 간단하게 들리 겠지만 실제로 이런 일이 벌어지는 이유에 관해서는 저를 잘 모릅니다.스프링 배치 작업에 대한 통합 테스트에 실패했습니다.
그래서 스프링 일괄 처리를 사용하여 Amazon의 Simple Email Service를 사용하여 이메일을 전송합니다. 내 CustomItemProcessor
안에 @Autowired
을 사용하여 정상적으로 내 AmazonEmailService
서비스에 연결합니다. AmazonEmailService
클래스는 내 EmailSender
인터페이스를 구현합니다.
AmazonEmailService
은 실제로 Amazon Simple Email Service를 호출하여 작업을 수행하는 데 사용되는 @Autowired
입니다.
내 AmazonSimpleEmailServiceClient
빈은 내 루트-servlet.xml 파일에 정의되어
<bean id="amazonSimpleEmailServiceClient"
class="com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient">
<constructor-arg ref="basicAWSCredentials" />
</bean>
<bean id="basicAWSCredentials" class="com.amazonaws.auth.BasicAWSCredentials">
<constructor-arg index="0" value="${aws.accessKey}"/>
<constructor-arg index="1" value="${aws.secretKey}" />
</bean>
이 모든 것이 잘 작동합니다.
내 문제는 스프링 배치 작업 통합 테스트를 실행할 때입니다. 전자 메일을 보낼 때 응답하지 않습니다. 나의 로깅은 amazonSimpleEmailServiceClient.send(emailRequest)
에 대한 호출에서 실행이 멈추고 계속 진행되지 않음을 보여줍니다.
저를 절대적으로 모욕 한 점은 스프링 배치 작업을위한 통합 테스트를 실행하기 전에 AmazonEmailService
유닛 테스트를 실행하면 모든 것이 성공적으로 완료된다는 것입니다. 왜 이것이 사실인지 알고 싶습니다. 실제로 전자 메일을 전송하는 배치 작업에는 단일 스레드에서 실행되는 TaskExecutor
이 있다는 것을 언급 할만한 가치가 있습니다.
스프링 배치 작업의 통합 테스트는 작업이 수신하는 모든 입력에 대해 이메일을 성공적으로 생성하고 Amazon SES를 사용하여 해당 이메일을 보낼 수 있는지 확인합니다. 또한 올바르게 설정된 큐에 개체를 읽고 쓰는 지 테스트하지만 내 문제와 관련이 없습니다. AmazonEmailService
의 단위 테스트는 아마존 전자 메일 시뮬레이터에 3 개의 전자 메일을 보냅니다.
아래에 요약 된 클래스 다이어그램을 게시하여 어떻게 서로 달라 붙는 지 확인할 수 있습니다.
- EmailService 인터페이스는 내 자신의 인터페이스입니다.
- AmazonEmailService는 내 서비스입니다. 이 서비스는 실제로 Amazon에서받은 객체 인 AmazonSimpleEmailServiceClient를 사용하여 전자 메일을 전송합니다.
- CustomItemProcessor는 ItemProcessor 인터페이스를 구현하는 내 자신의 개체입니다. Spring 배치가 일괄 작업의 항목을 실제로 처리하기 위해 사용하는 것입니다. 전자 메일은이 클래스에서 생성되어 전송되어야합니다.
- AmazonEmailServiceTest는 아마 AmazonEmailService의 능력을 테스트하여 전자 메일을 실제로 보내는 단위 테스트 일뿐입니다. 나는이 바보 같은 문제가 발생하고 이유를 고려할 때 가정 할 수
것들 :
- 나는 제대로 Spring 애플리케이션 컨텍스트에서 실행하는 내 단위/통합 테스트를 구성했습니다.
AmazonEmailServiceTest
이 성공적으로 실행됩니다.- 간단한 JavaMail 이메일 발신자를 사용하면 스프링 배치 통합 테스트가 성공적으로 실행됩니다.
- My Spring Batch 작업, 응용 프로그램 컨텍스트 및 빈은 모든 다른 클래스에 대해 XML 구성을 사용하여 올바르게 구성되고 정의됩니다.
- 내 Amazon 자격 증명이 유효하고 올바르게 작동합니다.
- 클래스는 자동으로 올바르게 입력됩니다.
- 예외가 함께 테스트를 실행하면,
AmazonEmailService
의 동일한 인스턴스가 두 테스트에 autowire가되어 분리 또는AmazonEmailServiceTest
- 으로 어느 스프링 배치 테스트를 실행하려고의 모든 단계에서 발생되지 않고 (즉, 동일한 메모리 주소 두 테스트 모두)
- 실제 단위 테스트에 사용 된 자격 증명을 확인할 방법이 없습니다.
- XML 구성 내에 PropertyPlaceholderConfigurer를 올바르게 구성했습니다.
- 단위 테스트를 수동으로 중지 할 때까지
amazonSimpleEmailServiceClient.send(emailRequest)
에 대한 호출이 중단됩니다. - 관련 빈은 자동 와이어 링을위한 응용 컨텍스트에서 검색 할 수 있습니다.
클래스 나 설정과 같은 정보가 더 필요하면 언제든지 물어보십시오. 나는 문자 그대로 응답을 기다리는 내 컴퓨터 앞에 앉아있다.
AmazonEmailService :
@Service
public class AmazonEmailService implements EmailService {
@Autowired
private AmazonSimpleEmailServiceClient amazonSimpleEmailServiceClient;
@Override
public String sendEmail(Email email){
//build request using Builder pattern//
return amazonSimpleEmailServiceClient.sendEmail(emailRequest);
}
}
CustomItemProcessor :
public class CustomProcessQueueItemProcessor implements
ItemProcessor<Foo, Bar> {
@Autowired
private EmailService amazonEmailService;
@Override
public Bar process(Foo foo) throws Exception {
//generate email from Foo object//
String result = amazonEmailService.sendEmail(email);
//create Bar object from result//
return bar;
}
}
AmazonEmailServiceTest :
public class AmazonEmailServiceTest extends SpringTest{
@Autowired
private EmailService amazonEmailService;
@Test
public void testSendEmailSuccess() {
Email successEmail = MockObjectFactory.setTestSuccessEmail();
String emailResultId = amazonEmailService.sendEmail(successEmail);
assertNotNull("The returned emailResultId was null", emailResultId);
}
}
나는 Spring 애플리케이션 컨텍스트에서 실행되도록 내 단위 테스트를 구성하는 곳 SpringTest 클래스입니다. My MockObjectFactory는 이름에서 알 수 있듯이 테스트 객체를 생성하는 정적 메서드가 포함 된 클래스입니다.
배치 서블릿 :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:batch="http://www.springframework.org/schema/batch"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2.xsd">
<import resource="jobs/fill-queue-job.xml" />
<import resource="jobs/process-queue-job.xml" />
<batch:job-repository id="jobRepository"
data-source="dataSource" transaction-manager="transactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor" ref="defaultTaskExecutor"></property>
</bean>
<bean id="jobRegistry"
class="org.springframework.batch.core.configuration.support.MapJobRegistry" />
<bean id="jobRegistryBeanPostProcessor"
class="org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobLoader"
class="org.springframework.batch.core.configuration.support.DefaultJobLoader">
<property name="jobRegistry" ref="jobRegistry" />
</bean>
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="jobOperator"
class="org.springframework.batch.core.launch.support.SimpleJobOperator">
<property name="jobLauncher" ref="jobLauncher" />
<property name="jobRepository" ref="jobRepository" />
<property name="jobRegistry" ref="jobRegistry" />
<property name="jobExplorer" ref="jobExplorer" />
</bean>
<bean id="domainObjectIdQueue" class="java.util.concurrent.ConcurrentLinkedQueue" />
<bean id="mainQueue" class="java.util.concurrent.ConcurrentLinkedQueue" />
<bean id="notificationQueue" class="java.util.concurrent.ConcurrentLinkedQueue" />
<bean id="fillQueueItemReader"
class="au.com.mail.batch.itemreaders.CustomServiceItemReader"
scope="step">
<constructor-arg ref="emailTaskServiceImpl" />
</bean>
<bean id="fillQueueItemProcessor"
class="au.com.mail.batch.itemprocessors.CustomFillQueueItemProcessor"
scope="step" />
<bean id="fillQueueCompositeItemWriter"
class="org.springframework.batch.item.support.CompositeItemWriter">
<property name="delegates">
<list>
<bean id="fillQueueItemWriter"
class="au.com.mail.batch.itemwriters.CustomQueueItemWriter"
scope="step" />
<bean id="emailTaskItemWriter"
class="au.com.mail.batch.itemwriters.CustomServiceItemWriter"
scope="step">
<constructor-arg ref="emailTaskServiceImpl" />
</bean>
</list>
</property>
</bean>
<bean id="processQueueItemReader"
class="au.com.mail.batch.itemreaders.CustomQueueItemReader"
scope="step">
<constructor-arg>
<value type="java.lang.Class">au.com.mail.domainobject.messagewrappers.MainQueueMessageWrapper
</value>
</constructor-arg>
</bean>
<bean id="processQueueItemProcessor"
class="au.com.mail.batch.itemprocessors.CustomProcessQueueItemProcessor"
scope="step" />
<bean id="processQueueItemWriter"
class="au.com.mail.batch.itemwriters.CustomQueueItemWriter"
scope="step" />
<bean id="defaultTaskExecutor"
class="org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor">
<property name="threadCount" value="5" />
</bean>
<bean id="processQueueTaskExecutor"
class="org.springframework.scheduling.quartz.SimpleThreadPoolTaskExecutor">
<property name="threadCount" value="1" />
</bean>
<bean id="customStepExecutionListener" class="au.com.mail.batch.CustomStepExecutionListener"
scope="step" />
<bean id="jobLauncherTestUtils" class="org.springframework.batch.test.JobLauncherTestUtils">
<property name="job" ref="fillQueue" />
<property name="jobRepository" ref="jobRepository" />
<property name="jobLauncher" ref="jobLauncher" />
</bean>
</beans>
프로세스 대기열 작업 정의 :
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/batch"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/batch http://www.springframework.org/schema/batch/spring-batch-2.2.xsd">
<beans:bean id="repeatQueueOpenTasklet"
class="au.com.mail.batch.CustomQueueRetryTaskletImpl" scope="step" />
<job id="processQueue" job-repository="jobRepository">
<step id="getQueue">
<tasklet ref="repeatQueueOpenTasklet" task-executor="processQueueTaskExecutor">
</tasklet>
<end on="FAILED" exit-code="NOOP" />
<next on="*" to="sendEmail" />
</step>
<step id="sendEmail">
<tasklet task-executor="processQueueTaskExecutor">
<chunk reader="processQueueItemReader" processor="processQueueItemProcessor"
writer="processQueueItemWriter" commit-interval="5" />
</tasklet>
<listeners>
<listener ref="customStepExecutionListener"></listener>
</listeners>
</step>
</job>
</beans:beans>
BIG BIG BIG UPDATE : 나는 jobLauncher
에서 defaultTaskExecutor
를 제거 내 processQueueTaskExecutor
프로세스 큐 태스크 릿에 제거 Amazon 서비스 호출이 성공합니다. 이제는 왜 이것이 사실인지 알 필요가 있습니다.
AWS 서비스는 AmazonSimpleEmailServiceClient의 후속 사용이 인증을 건너 뛰고 더 빨리 작동 할 수있게 해주는 "remember-me"인증 메커니즘과 유사한 방식 일 수 있습니다. 두 번째 생각은 AWS 서비스가 블로킹이고 JUnit이 실수가 아닌 경우 별도의 스레드에서 각 테스트를 실행한다는 것입니다. 나는 둘 사이의 연결을 보지 못했지만, 그것은 단지 생각이다. –
@AndreiStefan - 내가 생각했던 것과 비슷하지만 15 분 동안 Batch Jobs 테스트를 실행하고 아무데도 나가지 못했습니다. 또한 제 2 일괄 작업 중 단 하나만 실제로 Amazon 이메일 서비스를 공격합니다. AWS는 서비스 호출이 완료 될 때까지 차단하지만, 서비스가 시작되었거나 오류 메시지가 다시 나타나는지 확인하는 방법을 모릅니다. 나는 나보다 다른 누군가가 이것이 정말로 이상한 오류라고 생각하기 때문에 기쁘다. – JamesENL
여러 구성 요소가 어떻게 협력하는지 테스트 중이므로 * 통합 * 테스트가 아닌 * 단위 테스트가 유용 할 수 있습니다. – kryger