好吧,我知道這聽起來很簡單,但它確實讓我難以理解爲什麼會出現這種情況。Spring批處理作業失敗的集成測試
所以,我使用Spring Batch的生成電子郵件使用亞馬遜的簡單電子郵件服務發送。在我的CustomItemProcessor
內部,我正在使用@Autowired
正常連接我的AmazonEmailService
服務。 AmazonEmailService
類實現我的EmailSender
接口。
AmazonEmailService
有一個@Autowired
AmazonSimpleEmailServiceClient
它用於實際調用Amazon Simple Email Service來執行操作。
我AmazonSimpleEmailServiceClient
bean是在我的根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)
時停止,並且不會繼續。
讓我完全失望的事情是,如果我在爲我的Spring批處理作業運行集成測試之前對AmazonEmailService
運行單元測試,那麼所有事情都會成功完成。我想知道爲什麼是這樣。值得一提的是,實際發送電子郵件的批量作業在單個線程上運行TaskExecutor
。
爲Spring批處理作業集成測試,應該驗證作業成功生成每次它接收輸入,並且這些電子郵件可以使用Amazon SES發送的電子郵件。它還測試它是否正確讀取和寫入對象到已設置的隊列,但這與我的問題無關。 AmazonEmailService
的單元測試只發送3封電子郵件到亞馬遜郵件模擬器。
我會後下一個刪節的類圖,所以你能看到的東西是如何粘在一起。
- 的EmailService接口我自己的接口。
- 的AmazonEmailService是我自己的服務。該服務實際上使用亞馬遜給我的對象AmazonSimpleEmailServiceClient執行發送電子郵件。
- CustomItemProcessor是我自己的實現ItemProcessor接口的對象。這是Spring Batch用於實際處理批處理作業中的項目的內容。電子郵件應該由這個班級生成併發送。
- 的AmazonEmailServiceTest只是一個單元測試,測試AmazonEmailService的能力 實際發送電子郵件。
考慮的事情,爲什麼我有這個愚蠢的問題時,你可以假設:
- 我已經正確配置我單位/集成測試在Spring應用程序上下文中運行。
AmazonEmailServiceTest
本身成功運行。- 如果我恢復使用簡單的JavaMail電子郵件發件人,則Spring批量集成測試會成功運行。
- 我的Spring批處理作業,應用程序上下文和bean被正確配置並使用XML config爲我所有其他類定義。
- 我的亞馬遜憑證有效且正常工作。
- 類自動裝入正確。
- 也不例外是在試圖無論單獨地或與
AmazonEmailServiceTest
- 當運行測試在一起時,
AmazonEmailService
的同一個實例被自動連接到這兩個測試運行Spring Batch的測試任何階段拋出(即相同的內存地址對於兩個測試等) - 我無法檢查在實際單元測試中使用的憑證。
- 我已經正確配置了XML配置中的PropertyPlaceholderConfigurer。
- 對
amazonSimpleEmailServiceClient.send(emailRequest)
的呼叫將掛起,直到我手動停止單元測試。 - 有關的bean可在自動裝配的應用程序上下文中發現。
如果您需要更多信息,如類或配置,請不要猶豫,問。我真的坐在我的電腦前等待回覆。
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);
}
}
的SpringTest類就是配置我的單元測試,在Spring應用程序上下文中運行。我的MockObjectFactory,就是它的名字所暗示的,它是一個包含生成測試對象的靜態方法的類。
批次的Servlet:
<?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>
大大大UPDATE: 我從jobLauncher
除去在處理隊列微進程我processQueueTaskExecutor
和除去defaultTaskExecutor
並且亞馬遜服務調用成功。現在我只需要知道爲什麼會這樣。
只是大聲猜測並大聲思索:也許AWS服務有一種類似於「記住我」身份驗證機制的方式,該機制允許後續使用AmazonSimpleEmailServiceClient更快地工作,有點會跳過身份驗證。第二個想法是,如果我沒有弄錯,AWS服務是一個阻塞的,JUnit在一個單獨的線程中執行每個測試。我沒有看到兩者之間的聯繫,但這只是一個想法。 –
@AndreiStefan - 這與我的想法很相似,但我已將Batch Jobs測試運行15分鐘,並且沒有任何進展。而且我的兩個批處理作業中唯一一個實際上是擊中亞馬遜電子郵件服務。在服務調用完成之前,AWS會阻塞,但我不知道如何檢查它是否已啓動或收到任何錯誤消息。我也很高興,除了我以外的人認爲這是一個非常奇怪的錯誤。 – JamesENL
可能值得注意的是,這些是* integration *測試,而不是* unit *測試,因爲您正在測試多個組件如何合作。 – kryger