If you want to configure the context objects in the workflow implementation through Spring XML configuration, then use the bean names declared in the WorkflowScopeBeanNames class in the com.amazonaws.services.simpleworkflow.flow.spring package. For example:
<!-- workflow implementation -->
<bean id="workflowImpl" class="asadj.spring.test.MyWorkflowImpl" scope="workflow">
<property name="client" ref="activitiesClient"/>
<property name="clock" ref="workflowClock"/>
<property name="activityClient" ref="genericActivityClient"/>
<property name="dcContext" ref="decisionContext"/>
<property name="workflowClient" ref="genericWorkflowClient"/>
<property name="wfContext" ref="workflowContext"/>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
Alternatively, you may inject a DecisionContextProvider in the workflow implementation bean and use it to create the context. This can be useful if you want to provide custom implementations of the provider and context.
Injecting Resources in Activities
You can instantiate and configure activity implementations using an Inversion of Control (IoC) container and easily inject resources like database connections by declaring them as properties of the activity implementation class. Such resources will typically be scoped as singletons. Note that activity implementations are called by the activity worker on multiple threads. Therefore, access to shared resources must be synchronized.
JUnit Integration
The framework provides JUnit extensions as well as test implementations of the context objects, such as a test clock, that you can use to write and run unit tests with JUnit. With these extensions, you can test your workflow implementation locally inline.
Writing a Simple Unit Test
In order to write tests for your workflow, use the WorkflowTest class in the
com.amazonaws.services.simpleworkflow.flow.junit package. This class is a framework-specific JUnit MethodRule implementation and runs your workflow code locally, calling activities inline as opposed to going through Amazon SWF. This gives you the flexibility to run your tests as frequently as you desire without incurring any charges.
In order to use this class, simply declare a field of type WorkflowTest and annotate it with the @Rule annotation. Before running your tests, create a new WorkflowTest object and add your activity and workflow implementations to it. You can then use the generated workflow client factory to create a client and start an execution of the workflow. The framework also provides a custom JUnit runner, FlowBlockJUnit4ClassRunner, that you must use for your workflow tests. For example:
@RunWith(FlowBlockJUnit4ClassRunner.class) public class BookingWorkflowTest {
@Rule
JUnit Integration
List<String> trace;
private BookingWorkflowClientFactory workflowFactory = new BookingWorkflowClientFactoryImpl();
@Before
public void setUp() throws Exception { trace = new ArrayList<String>();
// Register activity implementation to be used during test run BookingActivities activities = new BookingActivitiesImpl(trace);
workflowTest.addActivitiesImplementation(activities);
workflowTest.addWorkflowImplementationType(BookingWorkflowImpl.class);
} @After
public void tearDown() throws Exception { trace = null;
} @Test
public void testReserveBoth() {
BookingWorkflowClient workflow = workflowFactory.getClient();
Promise<Void> booked = workflow.makeBooking(123, 345, true, true);
List<String> expected = new ArrayList<String>();
expected.add("reserveCar-123");
expected.add("reserveAirline-123");
expected.add("sendConfirmation-345");
AsyncAssert.assertEqualsWaitFor("invalid booking", expected, trace, booked);
} }
You can also specify a separate task list for each activity implementation that you add to
WorkflowTest. For example, if you have a workflow implementation that schedules activities in host-specific task lists, then you can register the activity in the task list of each host:
for (int i = 0; i < 10; i++) { String hostname = "host" + i;
workflowTest.addActivitiesImplementation(hostname,
new ImageProcessingActivities(hostname));
}
Notice that the code in the @Test is asynchronous. Therefore, you should use the asynchronous workflow client to start an execution. In order to verify the results of your test, an AsyncAssert help class is also provided. This class allows you to wait for promises to become ready before verifying results.
In this example, we wait for the result of the workflow execution to be ready before verifying the test output.
If you are using Spring, then the SpringWorkflowTest class can be used instead of the WorkflowTest class. SpringWorkflowTest provides properties that you can use to configure activity and workflow implementations easily through Spring configuration. Just like the Spring-aware workers, you should use the WorkflowScope to configure workflow implementation beans. This ensures that a new workflow implementation bean is created for every decision task. Make sure to configure these beans with the scoped-proxy proxy-target-class setting set to false. See the Spring Integration section for more details. The example Spring configuration shown in the Spring Integration section can be changed to test the workflow using SpringWorkflowTest:
<?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:aop="http://
www.springframework.org/schema/aop"
JUnit Integration
<!-- register custom workflow scope -->
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="workflow">
<bean
class="com.amazonaws.services.simpleworkflow.flow.spring.WorkflowScope" />
</entry>
</map>
</property>
</bean>
<context:annotation-config />
<bean id="accesskeys" class="com.amazonaws.auth.BasicAWSCredentials">
<constructor-arg value="{AWS.Access.ID}" />
<constructor-arg value="{AWS.Secret.Key}" />
</bean>
<bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
<property name="socketTimeout" value="70000" />
</bean>
<!-- Amazon SWF client -->
<bean id="swfClient"
class="com.amazonaws.services.simpleworkflow.AmazonSimpleWorkflowClient">
<constructor-arg ref="accesskeys" />
<constructor-arg ref="clientConfiguration" />
<property name="endpoint" value="{service.url}" />
</bean>
<!-- activities client -->
<bean id="activitiesClient" class="aws.flow.sample.MyActivitiesClientImpl"
scope="workflow">
</bean>
<!-- workflow implementation -->
<bean id="workflowImpl" class="aws.flow.sample.MyWorkflowImpl"
scope="workflow">
<property name="client" ref="activitiesClient" />
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<!-- WorkflowTest -->
<bean id="workflowTest"
class="com.amazonaws.services.simpleworkflow.flow.junit.spring.SpringWorkflowTest">
<property name="workflowImplementations">
<list>
<ref bean="workflowImpl" />
</list>
</property>
<property name="taskListActivitiesImplementationMap">
<map>
JUnit Integration
</beans>