Enterprise Architect in HCL Technologies a $7Billion IT services organization. My role is to work as a Technology Partner for large enterprise customers providing them low cost opensource solutions around Java, Spring and vFabric stack. I am also working on various projects involving, Cloud base solution, Mobile application and Business Analytics around Spring and vFabric space. Over 23 yrs, I have build repository of technologies and tools I liked and used extensively in my day to day work. In this blog, I am putting all these best practices and tools so that it will help the people who visit my website. Krishna is a DZone MVB and is not an employee of DZone and has posted 64 posts at DZone. You can read more from them at their website. View Full User Profile

JUnit Testing of Spring MVC application: Testing Spring Webflow

03.06.2013
| 5049 views |
  • submit to reddit

For people in hurry get the latest code from Github and run the below command,

mvn clean test -Dtest=com.example.bookstore.web.controller.CreateOrderTest

In continuation of my earlier blogs on Introduction to Spring MVC and Testing Frontend using Selenium in Spring MVC, in this blog I will demonstrate how to test Web layer in Spring MVC and Spring Web Flow. If you want to measure the quality of JUnit testing of your Spring Web Flow application refer Measuring navigation flow JUnit test in Spring Web flow. There is another blog I have written on JUnit testing Spring Web Flow refer JUnit testing with Spring Webflow.

In this blog I will demo how to build a Spring Web Flow based application using Test driven development (TDD). The use case I will be talking of is a web flow where in user drills down thru few categories and selects few books and select delivery options and place an order.

There is a good introduction of Spring Web Flow here. But just to give a brief introduction about Spring Web Flow, it is composed of a set of states. A state is a point in the flow where some event happens: for instance showing books list for a category or adding the books to the shopping cart. Each state has one or more transitions that are used to move to another state. A transition is triggered by an event.

As a first step, please refer this createOrders-flow.xml, if you notice carefully OrderForm is stored in the flowScope. And this object carries the state between each step. Let us see the bootstrapping process in JUnit test as below,

public class CreateOrderTest extends AbstractXmlFlowExecutionTests {
private OrderController orderController;

protected void setUp() {
orderController = Mockito.mock(OrderController.class);
}

@Override
protected void configureFlowBuilderContext(MockFlowBuilderContext builderContext) {
builderContext.registerBean("orderController", orderController);
}

@Override
protected FlowDefinitionResource getResource(FlowDefinitionResourceFactory resourceFactory) {
return resourceFactory.createFileResource("src/main/webapp/WEB-INF/view/public/createOrders/createOrders-flow.xml");
}

//JUnit tests below
}

If you notice this class is subclassed from a class AbstractXmlFlowExecutionTests, it has 3 overridden methods, setup, configureFlowBuilderContext, getResource. getResource method is the one where we set the createOrders-flow.xml and is returned for testing. If you also notice we have used Mockito to mock OrderController and register the bean in the flow XML.

Let us start building the JUnit test to test each node (view-state) of the flow. For starting the flow you need to do as below,

public void testStartCreateOrderFlow() {

MutableAttributeMap input = new LocalAttributeMap();
MockExternalContext context = new MockExternalContext();
startFlow(input, context);

Mockito.verify(orderController, VerificationModeFactory.times(1)).initializeForm();
Mockito.verify(orderController, VerificationModeFactory.times(1)).initializeSelectableCategories();

assertCurrentStateEquals("selectCategory");
assertResponseWrittenEquals("selectCategory", context);
}

If you notice carefully in the flow, we are asking the controller to call the initializeForm at on-start event and call initializeSelectableCategories at on-render of selectCategory view-state.

Using Mockito we are verifying if the initializeForm and initializeSelectableCategories called once only. And finally we assert for the next state.

Similarly we test other view states.

Finally we test calling a subflow by mocking the subflow, refer createMockBookingSubflow below you will get the idea. If you want to understand more refer Mocking a subflow.

public void testSelectDeliveryOptions_AddAndNext() {

setCurrentState("selectDeliveryOptions");
 getFlowScope().put("orderForm", createTestOrderForm());
 getFlowDefinitionRegistry().registerFlowDefinition(createMockBookingSubflow());

MockExternalContext context = new MockExternalContext();
 context.setEventId("finish");
 resumeFlow(context);
 }

private Flow createMockBookingSubflow() {
 Flow mockBookingFlow = new Flow("secured/placeOrders");
 mockBookingFlow.setInputMapper(new Mapper() {
 public MappingResults map(Object source, Object target) {
 // assert that 1L was passed in as input
 return null;
 }
 });
 // immediately return the bookingConfirmed outcome so the caller can respond
 new EndState(mockBookingFlow, "endOrderOk");
 return mockBookingFlow;
 }

I hope this blog helped you. In my next blog I will be taking about integrating Kickstrap with Spring MVC application.

Reference:

Pro Spring MVC: With Web Flow by by Marten Deinum, Koen Serneels





 

Published at DZone with permission of Krishna Prasad, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)