2014-04-15 59 views
2

好吧,我知道這聽起來很簡單,但它確實讓我難以理解爲什麼會出現這種情況。Spring批處理作業失敗的集成測試

所以,我使用Spring Batch的生成電子郵件使用亞馬遜的簡單電子郵件服務發送。在我的CustomItemProcessor內部,我正在使用@Autowired正常連接我的AmazonEmailService服務。 AmazonEmailService類實現我的EmailSender接口。

AmazonEmailService有一個@AutowiredAmazonSimpleEmailServiceClient它用於實際調用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封電子郵件到亞馬遜郵件模擬器。

我會後下一個刪節的類圖,所以你能看到的東西是如何粘在一起。

Class Diagram

  • 的EmailService接口我自己的接口
  • 的AmazonEmailService是我自己的服務。該服務實際上使用亞馬遜給我的對象AmazonSimpleEmailServiceClient執行發送電子郵件。
  • CustomItemProcessor是我自己的實現ItemProcessor接口的對象。這是Spring Batch用於實際處理批處理作業中的項目的內容。電子郵件應該由這個班級生成併發送。
  • 的AmazonEmailServiceTest只是一個單元測試,測試AmazonEmailService的能力 實際發送電子郵件。

考慮的事情,爲什麼我有這個愚蠢的問題時,你可以假設:

  1. 我已經正確配置我單位/集成測試在Spring應用程序上下文中運行。
  2. AmazonEmailServiceTest本身成功運行。
  3. 如果我恢復使用簡單的JavaMail電子郵件發件人,則Spring批量集成測試會成功運行。
  4. 我的Spring批處理作業,應用程序上下文和bean被正確配置並使用XML config爲我所有其他類定義。
  5. 我的亞馬遜憑證有效且正常工作。
  6. 類自動裝入正確。
  7. 也不例外是在試圖無論單獨地或與AmazonEmailServiceTest
  8. 當運行測試在一起時,AmazonEmailService的同一個實例被自動連接到這兩個測試運行Spring Batch的測試任何階段拋出(即相同的內存地址對於兩個測試等)
  9. 我無法檢查在實際單元測試中使用的憑證。
  10. 我已經正確配置了XML配置中的PropertyPlaceholderConfigurer。
  11. amazonSimpleEmailServiceClient.send(emailRequest)的呼叫將掛起,直到我手動停止單元測試。
  12. 有關的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並且亞馬遜服務調用成功。現在我只需要知道爲什麼會這樣。

+1

只是大聲猜測並大聲思索:也許AWS服務有一種類似於「記住我」身份驗證機制的方式,該機制允許後續使用AmazonSimpleEmailServiceClient更快地工作,有點會跳過身份驗證。第二個想法是,如果我沒有弄錯,AWS服務是一個阻塞的,JUnit在一個單獨的線程中執行每個測試。我沒有看到兩者之間的聯繫,但這只是一個想法。 –

+0

@AndreiStefan - 這與我的想法很相似,但我已將Batch Jobs測試運行15分鐘,並且沒有任何進展。而且我的兩個批處理作業中唯一一個實際上是擊中亞馬遜電子郵件服務。在服務調用完成之前,AWS會阻塞,但我不知道如何檢查它是否已啓動或收到任何錯誤消息。我也很高興,除了我以外的人認爲這是一個非常奇怪的錯誤。 – JamesENL

+2

可能值得注意的是,這些是* integration *測試,而不是* unit *測試,因爲您正在測試多個組件如何合作。 – kryger

回答

1

好吧,我終於整理出來了。這與我的單元/集成測試以及與多線程Spring Batch任務執行程序無關。任務執行者調用Amazon電子郵件服務,而名爲AwsSdkMetrics的類在主線程中鎖定了名爲useDefaultMetrics的同步方法。這意味着執行無法在任務執行程序內進行,因此它等待主線程釋放該同步方法。

因此,我從ANT JVM中fork了我的jUnit JVM,並且所有東西都像魅力一樣工作。

2

從您的文章中不清楚您的「單元」測試實際上是在嘗試測試。假設亞馬遜的電子郵件服務已經過很好的測試,並且不需要在單元測試中進行實際測試。

相反,你可以創建你的單元測試,提供了EmailService的模擬一個新的測試Spring上下文,然後在你的單元測試驗證emailService.sendEmail(...)方法實際上是被調用的時候,你希望它是,與內容你的期望。這樣您的測試永遠不需要與任何實際的電子郵件服務交互。

+0

對不起,我的單元測試的重點是驗證Spring Batch作業是否確實處理了收到的所有郵件,並且這些郵件實際上是使用Amazon SES發送的。這個測試失敗的事實是一個嚴重的問題,因爲我需要確保SES可以用來發送這些電子郵件。 – JamesENL

+0

我更新了我的問題,以包括批處理作業單元測試應該測試的內容 – JamesENL

+0

和EmailService界面是我自己的界面,亞馬遜沒有給我這個給我 – JamesENL

相關問題