Java Spring批处理作业的集成测试失败

Java Spring批处理作业的集成测试失败,java,spring,integration-testing,spring-batch,amazon-ses,Java,Spring,Integration Testing,Spring Batch,Amazon Ses,好的,我知道这听起来很简单,但它确实让我困惑,为什么会发生这种情况 所以,我使用SpringBatch生成电子邮件,通过Amazon的简单电子邮件服务发送。在我的CustomItemProcessor中,我使用@Autowired正常连接我的AmazonEmailService服务。AmazonEmailService类实现了我的EmailSender接口 AmazonEmailService有一个@AutowiredAmazonSimpleEmailServiceClient,它用来实际调用A

好的,我知道这听起来很简单,但它确实让我困惑,为什么会发生这种情况

所以,我使用SpringBatch生成电子邮件,通过Amazon的简单电子邮件服务发送。在我的
CustomItemProcessor
中,我使用
@Autowired
正常连接我的
AmazonEmailService
服务。
AmazonEmailService
类实现了我的
EmailSender
接口

AmazonEmailService
有一个
@Autowired
AmazonSimpleEmailServiceClient
,它用来实际调用Amazon简单电子邮件服务来完成任务

我的
AmazonSimpleEmailServiceClient
bean在我的root-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>
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;
    }
}
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>

au.com.mail.domainobject.messagewrappers.MainQueueMessageWrapper
进程队列作业定义:

<?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>

大更新:
我在process queue tasklet上删除了我的
processQueueTaskExecutor
,并从
jobLauncher
中删除了
defaultTaskExecutor
,Amazon服务调用成功。现在我只想知道为什么会出现这种情况。

从你的帖子中不清楚你的“单元”测试实际上要测试什么。可以安全地假设Amazon的电子邮件服务经过良好测试,不需要在单元测试中进行实际测试


相反,您可以为单元测试创建一个新的测试spring上下文,该上下文提供了一个对
EmailService
的模拟,然后在单元测试中验证
EmailService.sendEmail(…)
方法是否在您预期的时间调用,以及您预期的内容。这样,您的测试就不需要与任何实际的电子邮件服务进行交互。

好的,我终于解决了所有问题。这与我的单元/集成测试无关,而与多线程Spring批处理任务执行器有关。任务执行者正在调用Amazon电子邮件服务,而一个名为AwsSdkMetrics的类在主线程中锁定了一个名为useDefaultMetrics的同步方法。这意味着执行无法在任务执行器内部进行,因此它将挂起,等待主线程释放该同步方法


因此,我将jUnit JVM从ANT JVM中分离出来,一切都开始正常工作。

对不起,我的单元测试的全部目的是验证Spring批处理作业是否确实将收到的所有项目处理为电子邮件,以及这些电子邮件是否确实使用Amazon SES发送。这个测试失败的事实是一个严重的问题,因为我需要确保SES可以用来发送这些电子邮件。我更新了我的问题,包括对批处理作业单元测试应该测试什么的描述,EmailService接口是我自己的接口,亚马逊没有让我这么做,只是猜测和大声思考:也许AWS服务有一种类似于“记住我”的身份验证机制的方式,允许后续使用AmazonSimpleEmailServiceClient更快地工作,在某种程度上跳过了身份验证。第二个想法是AWS服务是一个阻塞服务,如果我没有弄错的话,JUnit在一个单独的线程中执行每个测试。我看不出两者之间有什么联系,不过,这只是一个想法。@andrestefan-这与我的想法类似,但我让批处理作业测试运行了15分钟,结果一无所获。而且,我的两个批处理作业中唯一一个实际上是访问亚马逊电子邮件服务。AWS会一直阻塞直到服务调用完成,但我不知道如何检查它是否已启动或返回任何错误消息。我也很高兴其他人认为这是一个非常奇怪的错误。也许值得注意的是,这些是集成测试,不是单元测试,因为您正在测试多个组件是如何协作的。您能发布作业和相关bean的配置吗?@MichaelMinella这可能是因为我的CustomItemProcessor是步骤范围的吗?这对我来说没有意义,因为bean正在自动连接,但可能没有正确地自动连接或其他什么。
<?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>