Search code examples
javaunit-testingamazon-web-servicesamazon-swf

Unit testing SWF with @Asynchronous methods


I'm trying to test a workflow that has an asynchronous method - setup roughly looks like this:

@RunWith(FlowBlockJUnit4ClassRunner.class)
public class testWorkflow {
    @Rule
    public WorkflowTest workflowTest = new WorkflowTest();
    @Mock
    protected Activities mockActivities;

    @Before
    public void setUp() throws Exception {
        workflowTest.addActivitiesImplementation(mockActivities);
        workflowTest.addWorkflowImplementationType(Workflow.class);

        workflow = workflowFactory.getClient();
    }

    @Test
    public void testMethod1Exception() throws Throwable {
        doThrow(new RuntimeException("bang!"))
                .when(mockActivities).method1();

        try {
            runWorkflow();
            fail();
        } catch (Exception e) {
            verify(mockActivities, never()).method2();
        }
    }

    private void runWorkflow() throws Throwable {
        AsyncScope scope = new AsyncScope() {
            @Override
            protected void doAsync() {
                workflow.run();
            }
        };
        scope.eventLoop();
        if (!scope.isComplete()) {
            System.out.println(scope.getAsynchronousThreadDumpAsString());
        }
    }
}

My problem is in my code the workflow looks roughly like this:

public class Workflow {
    public void run() {
        final Promise<Result> pResult = client.method1();
        doAsync(pResult);
    }

    @Asynchronous
    public void doAsync(Promise<Result> pResult) {
       ...
    }
}

I found that the unit test hits the fail() call. Reading the output of the getAsynchronousThreadDumpAsString method seems to indicate that the workflow is waiting on pResult being available for the @Asynchronous method, however it doesn't become available because I threw the exception. Is there any way to get a test like this to work? The actual test I am trying to get to work is testing how code behaves in a doCatch block, but the workflow seems to freeze due to the @Asynchronous method call.


Solution

  • I believe the problem is that you use both AsyncScope and WorkflowTest rule at the same time. WorkflowTest already executes @Test method in an AsyncScope, so another AsyncScope might confuse it. I would rewrite your code like:

    @RunWith(FlowBlockJUnit4ClassRunner.class)
    public class testWorkflow {
        @Rule
        public WorkflowTest workflowTest = new WorkflowTest();
        @Mock
        protected Activities mockActivities;
    
        @Before
        public void setUp() throws Exception {
            workflowTest.addActivitiesImplementation(mockActivities);
            workflowTest.addWorkflowImplementationType(Workflow.class);
    
            workflow = workflowFactory.getClient();
        }
    
        @Test
        public void testMethod1Exception() throws Throwable {
            doThrow(new RuntimeException("bang!"))
                    .when(mockActivities).method1();
            new TryCatchFinally() {
               Throwable failure;
    
               protected void doTry() {
                    workflow.run();
               }
    
               protected void doCatch(Throwable e) {
                  failure = e;
               }
    
               protected void doFinally() {
                  assertNotNull(failure);
               }
            };
        }
    }
    

    But in reality even all of the above is not necessary when using expected:

    @RunWith(FlowBlockJUnit4ClassRunner.class)
    public class testWorkflow {
        @Rule
        public WorkflowTest workflowTest = new WorkflowTest();
        @Mock
        protected Activities mockActivities;
    
        @Before
        public void setUp() throws Exception {
            workflowTest.addActivitiesImplementation(mockActivities);
            workflowTest.addWorkflowImplementationType(Workflow.class);
    
            workflow = workflowFactory.getClient();
        }
    
        @Test(expected=RuntimeException.class)
        public void testMethod1Exception() throws Throwable {
            doThrow(new RuntimeException("bang!"))
                    .when(mockActivities).method1();
            workflow.run();
        }
    }
    

    Code inside @Test methods is executed in a context of a dummy workflow. That's why, for example, inside tests workflow is created using normal client and not external one.