added advanced-testing-examples

This commit is contained in:
David Turanski
2011-07-21 15:42:36 -04:00
parent 2df45b703f
commit 508a3484e7
9 changed files with 439 additions and 2 deletions

5
.gitignore vendored
View File

@@ -1 +1,6 @@
application.log
target/
.settings/
log.roo
.project
.classpath

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>advanced-testing-examples</artifactId>
<version>2.0.0</version>
<name>Spring Integration Advanced Testing Examples</name>
<packaging>jar</packaging>
<properties>
<spring.integration.version>2.0.5.RELEASE</spring.integration.version>
<spring.version>3.0.5.RELEASE</spring.version>
<log4j.version>1.2.16</log4j.version>
<junit.version>4.7</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-jms</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-groovy</artifactId>
<version>${spring.integration.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- test-scoped dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jms_1.1_spec</artifactId>
<version>1.1.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.8.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<testResources>
<testResource>
<directory>src/test/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
<testResource>
<directory>src/test/resources</directory>
<includes>
<include>**/*</include>
</includes>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<compilerArgument>-Xlint:all</compilerArgument>
<showWarnings>true</showWarnings>
<showDeprecation>false</showDeprecation>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>repository.springframework.maven.release</id>
<name>Spring Framework Maven Release Repository</name>
<url>http://maven.springframework.org/release</url>
</repository>
<repository>
<id>repository.springframework.maven.milestone</id>
<name>Spring Framework Maven Milestone Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
<repository>
<id>repository.springframework.maven.snapshot</id>
<name>Spring Framework Maven Snapshot Repository</name>
<url>http://maven.springframework.org/snapshot</url>
</repository>
</repositories>
</project>

View File

@@ -0,0 +1,14 @@
Example test cases that show advanced techniques to test Spring Integration applications.
For basic testing examples see basic/testing-examples
Examples
========
...jms.JmsMockTests.java
This test case shows how to test an integration flow that uses JMS inbound channel adapter
by using Mockito to mock a JmsTemplate (and dependent JMS objects). The example flow in
src/main/resources/integration-config.xml does not depend on JMS but includes some additional
error handling on errorChannel. The errorChannel is configured on the JMS adapter. So
We want test the entire flow for cases in which an invalid message is received via JMS and routed
to errorChannel. How do we do this without requiring a JMS message broker?

View File

@@ -0,0 +1,33 @@
<?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:int="http://www.springframework.org/schema/integration"
xmlns:int-groovy="http://www.springframework.org/schema/integration/groovy"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/groovy http://www.springframework.org/schema/integration/groovy/spring-integration-groovy-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
This implements the core business logic of the integration flow. Not
binding input and output channels to JMS (or any other protocol) within makes this flow more
easily testable.
-->
<!-- Throw an exception -->
<int:transformer input-channel="inputChannel" output-channel="outputChannel">
<int-groovy:script><![CDATA[
if (!payload.equals('hello')) {
throw new RuntimeException('invalid payload')
}
payload
]]>
</int-groovy:script>
</int:transformer>
<int:channel id="outputChannel"/>
<!-- Do some error handling -->
<int:transformer input-channel="errorChannel" output-channel="invalidMessageChannel" expression="payload.cause.cause.message"/>
<int:channel id="invalidMessageChannel"/>
</beans>

View File

@@ -0,0 +1,31 @@
<?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:int="http://www.springframework.org/schema/integration"
xmlns:int-jms="http://www.springframework.org/schema/integration/jms"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.0.xsd
http://www.springframework.org/schema/integration/jms http://www.springframework.org/schema/integration/jms/spring-integration-jms-2.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--
This is a test context that injects a Mock JMS template into a JMS
inbound channel adapter. The inbound-channel-adapter provides or overrides the 'real'
adapter for testing purposes.
If the flow imported flow throws an exception, an ErrorMessage containg the exception will be sent
to the error channel for additional handling.
-->
<import resource="classpath:integration-config.xml"/>
<int-jms:inbound-channel-adapter jms-template="jmsTemplate"
channel="inputChannel">
<int:poller fixed-rate="500" max-messages-per-poll="1" error-channel="errorChannel"/>
</int-jms:inbound-channel-adapter>
<bean id="jmsTemplate" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.jms.core.JmsTemplate"/>
</bean>
</beans>

View File

@@ -0,0 +1,188 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.integration.samples.advance.testing.jms;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.jms.JMSException;
import javax.jms.TextMessage;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.MessagingException;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.core.SubscribableChannel;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.SimpleMessageConverter;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class JmsMockTests {
@Autowired
JmsTemplate mockJmsTemplate;
@Autowired
@Qualifier("inputChannel")
MessageChannel inputChannel;
@Autowired
@Qualifier("outputChannel")
SubscribableChannel outputChannel;
@Autowired
@Qualifier("invalidMessageChannel")
SubscribableChannel invalidMessageChannel;
/**
* This test verifies that a message received on a polling JMS inbound channel adapter is
* routed to the designated channel and that the message payload is as expected
*
* @throws JMSException
* @throws InterruptedException
* @throws IOException
*/
@Test
public void testReceiveMessage() throws JMSException, InterruptedException, IOException {
String msg = "hello";
boolean sent = verifyJmsMessageReceivedOnOutputChannel(msg, outputChannel,new CountDownHandler() {
@Override
protected void verifyMessage(Message<?> message) {
assertEquals("hello",message.getPayload());
}
}
);
assertTrue("message not sent to expected output channel", sent);
}
/**
* This test verifies that a message received on a polling JMS inbound channel adapter is
* routed to the errorChannel and that the message payload is the expected exception
*
* @throws JMSException
* @throws IOException
* @throws InterruptedException
*/
@Test
public void testReceiveInvalidMessage() throws JMSException, IOException, InterruptedException {
String msg = "whoops";
boolean sent = verifyJmsMessageReceivedOnOutputChannel(msg, invalidMessageChannel,new CountDownHandler() {
@Override
protected void verifyMessage(Message<?> message) {
assertEquals("invalid payload",message.getPayload());
}
}
);
assertTrue("message not sent to expected output channel", sent);
}
/**
* Provide a message via a mock JMS template and wait for the default timeout to receive the message on the expected channel
* @param obj The message provided to the poller (currently must be a String)
* @param expectedOutputChannel The expected output channel
* @param handler An instance of CountDownHandler to handle (verify) the output message
* @return true if the message was received on the expected channel
* @throws JMSException
* @throws InterruptedException
*/
protected boolean verifyJmsMessageReceivedOnOutputChannel(Object obj, SubscribableChannel expectedOutputChannel, CountDownHandler handler) throws JMSException, InterruptedException{
return verifyJmsMessageOnOutputChannel(obj, expectedOutputChannel, handler, 5000);
}
/**
* Provide a message via a mock JMS template and wait for the specified timeout to receive the message on the expected channel
* @param obj The message provided to the poller (currently must be a String)
* @param expectedOutputChannel The expected output channel
* @param handler An instance of CountDownHandler to handle (verify) the output message
* @param timeoutMillisec The timeout period. Note that this must allow at least enough time to process the entire flow. Only set if the default is
* not long enough
* @return true if the message was received on the expected channel
* @throws JMSException
* @throws InterruptedException
*/
protected boolean verifyJmsMessageOnOutputChannel(Object obj, SubscribableChannel expectedOutputChannel, CountDownHandler handler,int timeoutMillisec) throws JMSException,
InterruptedException {
if (!(obj instanceof String)) {
throw new IllegalArgumentException("Only TextMessage is currently supported");
}
/*
* Use mocks to create a message returned to the JMS inbound adapter. It is assumed that the JmsTemplate
* is also a mock.
*/
TextMessage message = mock(TextMessage.class);
doReturn(new SimpleMessageConverter()).when(mockJmsTemplate).getMessageConverter();
doReturn(message).when(mockJmsTemplate).receiveSelected(anyString());
String text = (String) obj;
CountDownLatch latch = new CountDownLatch(1);
handler.setLatch(latch);
doReturn(text).when(message).getText();
expectedOutputChannel.subscribe(handler);
return latch.await(timeoutMillisec, TimeUnit.MILLISECONDS);
}
/*
* A MessageHandler that uses a CountDownLatch to syncronize with the calling thread
*/
private abstract class CountDownHandler implements MessageHandler {
CountDownLatch latch;
public final void setLatch(CountDownLatch latch){
this.latch = latch;
}
protected abstract void verifyMessage(Message<?> message);
/*
* (non-Javadoc)
*
* @see
* org.springframework.integration.core.MessageHandler#handleMessage
* (org.springframework.integration.Message)
*/
public void handleMessage(Message<?> message) throws MessagingException {
verifyMessage(message);
latch.countDown();
}
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<!-- Appenders -->
<appender name="console" class="org.apache.log4j.ConsoleAppender">
<param name="Target" value="System.out" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="[%t] %-5p: %c - %m%n" />
</layout>
</appender>
<!-- Loggers -->
<logger name="org.springframework.integration">
<level value="info" />
</logger>
<!-- Root Logger -->
<root>
<priority value="info" />
<appender-ref ref="console" />
</root>
</log4j:configuration>

16
advanced/pom.xml Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.integration.samples</groupId>
<artifactId>advanced-samples</artifactId>
<version>2.0.0</version>
<name>Advanced Spring Integration Samples</name>
<packaging>pom</packaging>
<modules>
<module>advanced-testing-examples</module>
</modules>
</project>

View File

@@ -1,11 +1,11 @@
$$ ADVANCED Samples $$
This category targets advanced develoopers who are well familiar with Spring Integration framework but looking to
This category targets advanced developers who are well familiar with Spring Integration framework but looking to
extend it to address a specific custom need by extending from Spring Integration public API.
For example; if you are looking for samples showing you how to implement a custom Channel or
Consumer (event-based or polling-based), or you trying to figure out what is the most appropriate way to implement
custom BeanParser on top of Spring Integration<6F>BeanParser<65>hierarchy when implementing custom name space,
custom BeanParser on top of Spring Integration<6F>BeanParser<65>hierarchy when implementing custom name space,
this would be the right place to look.
Here you can also find samples that will help you with adapter development. Spring Integration comes with an extensive
library of adapters that allow you to connect remote systems with Spring Integration messaging framework.