Tag Archives: Test Driven Development

Measuring navigation flow JUnit test in Spring Web flow

Currently I am exploring Spring Web flow. Someone has already written a utility for measuring how well you are testing the Spring Web Flow web application navigation flow using JUnit. In this blog I am extending this utility to work with Sonar. The working project is here and Sonar plugin is here @ Github.

For people in hurry,

  • Get the latest code
  • Build the Spring Web Flow Test Sonar plugin using “mvn package”
  • Deploy sonar plugin jar located in the target folder into Sonar extension location and Start sonar
  • Run “mvn test sonar:sonar” on Spring Web Flow Coverage project
  • Finally you can go to Sonar and see the report as below

Problem Statement: Measuring Spring Web flow test coverage

In any web application there will be a page navigation flow. Frameworks like Spring Web flow, can externalize the page navigation as well as session management between each page in the navigation. In the below example, there is a simple usecase, where in we are presented with a food menu. Based on the menu presented, we can drill down further to findout more or we can cancel if we are not interested.

Details

Based on the usecase there are overall 13 distinct navigation paths in the Spring Web Flow. We can simulate all these paths by first writing the JUnit test and use the library to keep track of all the paths tested. In our test case only 7 paths are covered, out of 13. The sonar report is as below,

Spring WebFlow flow coverage

Utility to measure the navigation flow covered in a web application

We will adopt the Test first approach, by building the JUnit test to test the flow as per this blog.  The psedocode is as below,

private XMLFlowCoveragePathListenerAdapter listener = new XMLFlowCoveragePathListenerAdapter("test-flow");

public void testStartFlow() {
XMLFlowCoveragePathListenerAdapter.setTestName("testStartFlow");
XMLFlowCoveragePathListenerAdapter.iniliatizePath();

//test and assert the flow
}

public void testNextItemInFlow() {
XMLFlowCoveragePathListenerAdapter.setTestName("testNextItemInFlow");
XMLFlowCoveragePathListenerAdapter.iniliatizePath();

//test and assert the flow
}

public void testSecondItemInFlow() {
XMLFlowCoveragePathListenerAdapter.setTestName("testSecondItemInFlow");
XMLFlowCoveragePathListenerAdapter.iniliatizePath();

//test and assert the flow

//FINALLY END THE COVERAGE
XMLFlowCoveragePathListenerAdapter.endCoverage();
}

Conclusion

This utility is in a pre-alpha state. Currently it only show information on how many navigation flows it has covered against the total number as a percentage. There are few enhancements we need to take care of. For example, we need to provide a drilldown on what are the navigation flow that is not covered. We also need to build a threshold, where if it is less than that, it should break the maven build. Ofcourse, we need to cleanup the code.

I feel even in its present form this utility is useful. Feel free to use it.

Advertisements

Designing a simple usecase using Test First Approach using Spring

In this blog I will demonstrate designing a usecase with Test First Development approach. Let me explain the usecase first, we need to write an application, where we need to recommend a user some products based on his past purchase history, in which he has similar brands. Get the working code from Github.

If you see in the above usecase, there are 3 objects, User, Item object, and UserItemPurchaseHistory.

Test First Development

As in the test first approach, let us write the test as below,

@Test
public void testUserRelevance() {

ArrayList<Item> productFromApiList = new ArrayList<Item>();

Item Item = new Item();
Item.setName("Ferrari 750 Monza");
Item.setBrand("Ferrari");
productFromApiList.add(Item);

Item = new Item();
Item.setName("Porsche 911 GT3");
Item.setBrand("Porsche");
productFromApiList.add(Item);

Item = new Item();
Item.setName("Cheverlet Corvette");
Item.setBrand("Cheverlet");
productFromApiList.add(Item);

Item = new Item();
Item.setName("Honda City");
Item.setBrand("Honda");
productFromApiList.add(Item);

Item = new Item();
Item.setName("Hynduai Verna");
Item.setBrand("Hynduai");
productFromApiList.add(Item);

HashMap<String, Object> map = new HashMap<String, Object>();

List<Item> productFromApiListOut = productResponseServiceImpl.filterAsPerUserRelevance(productFromApiList, 2);

assertEquals(productFromApiListOut.size(), 1);
assertEquals(productFromApiListOut.get(0).getName(),"Ferrari 750 Monza");
}

Now we need to do database configuration for testing purpose. We will use Spring to configure embedded inmemory database as follows,

<bean id="sqlMapClient">
<property name="configLocation" value="ibatis-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>

<bean id="ibatisTemplate">
<property name="sqlMapClient" ref="sqlMapClient" />
</bean>

<beans profile="dev">
<jdbc:embedded-database id="dataSource" type="HSQL">
<jdbc:script location="classpath:SourceDB.sql" />
</jdbc:embedded-database>
</beans>

If you notice, there is SourceDB.sql where you have schema design and the seed data. Once you created the config file, you need to wire the same with the JUnit test as below and set the profile,


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml",
"classpath:test-datasource-config.xml"})
public class FilterAsPerUserFriendsRelevanceTest {

static {
System.setProperty("spring.profiles.active", "dev");
}

The implementation details of  filterAsPerUserRelevance method as below,

public List<Item> filterAsPerUserRelevance(List<Item> productListFilteredAsperUserTaste, int id) {
List<Item> recommendedProducts = getListFromRecommendationEngine(id);

ArrayList<Item> list3 = new ArrayList<Item>();

for (Item productFromApi : productListFilteredAsperUserTaste) {
for (Item item : recommendedProducts) {
if (productFromApi.getBrand().toLowerCase().equals(item.getBrand().toLowerCase())) {
list3.add(productFromApi);
}
}
}
return list3;
}

private List<Item> getListFromRecommendationEngine(int id) {
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("userId", id);

List<UserItemPurchaseHistory> list2 = ibatisTemplate.queryForList("GoSmarter.userItemList", map);

assertEquals(1, list2.size());
assertEquals(list2.get(0).getItemId(), 1);

map = new HashMap<String, Object>();
map.put("itemId", list2.get(0).getItemId());
Item item = (Item) ibatisTemplate.queryForObject("GoSmarter.itemDetails", map);
assertEquals(item.getName(), "FERRARI ENZO");

List<Item> list3 = new ArrayList<Item>();
list3.add(item);
return list3;
}

If you notice we get a input list of the items the user has to choose from. It retrieves the user item purchase history and compared the item brands and return the brands which the user has already purchased and asserts in the JUnit test.

In the test first development, we are testing first and than design the POJO and the associated DB schema and write the business logic and assert the output as per the Seed data.

Once the design is complete, we use a different profile to point to the actual database.

Measuring flow channel JUnit test coverage in Spring Integration Flow

Test Coverage

In any enterprise integration project there has to be a Business Workflow that needs to be developed and should have proper test coverage. In one  of our largest Spring Integration project, we had to measure how well the Business workflow has test coverage. Spring Integration (SI) is a lightweight framework that has good unit testing support.

Gary Russell one of the leading Spring Integration Architects helped us in designing this framework. The framework does the following,

  • The framework has a class, you need to inherit this class in your SI to get JUnit Test Coverage test with this class. It will wire tap all the channels and count the total channels tested in the SI flow and calculates how many channels the tests are covering and will derive the coverage ratio.
  • We also implemented a Maven plugin, where in we can set the threshold for the build to succeed based on the coverage ratio
  • This framework also consists of the sonar plugin for channel coverage  (contributed by Gary to Github) for Sonar software quality tool. Once you get the plugin, you need to build the plugin as “mvn package”. Take the jar file from the target and copy it into <Sonar Home>/extensions/plugins.
  • Start Sonar
  • In your SI component, if you do “mvn sonar:sonar” it will, push the quality metrics information into Sonar

The report in Sonar looks as below,

Spring Integration Channel Coverage

Spring Integration Channel Coverage

The codebase for the utility class AbstractChannelCoverageTests is checked in @ github . You can see the test class @ src/test/java/org/springframework/integration/test/coverage/ChannelCoverageTests.java. If you notice, in your SI testclass you need to extend this class as follows,

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ChannelCoverageTests extends AbstractChannelCoverageTests {

@Test
void testChannel(){
//testing and asserting the flow
}

It will load a SI config file @ src/test/resources/org/springframework/integration/test/coverage/ChannelCoverageTests-context.xml and load all the channels and will start intercepting for any payload flow and keep count of it.

As per the above diagram, it will indicate how many channels are missed and it helps in making sure you cover those channels as well.

JUnit testing with Spring Webflow

Spring Webflow sample

I love SpringSource related technologies because of the supporting classes they provide for Test Driven Development (TDD). I do TDD for 2 reasons,

  • I design the objects using spring webflow application from nothing, writing the tests first and evolving the code efficiently to support the tests and making the test success and finally the building the application
  • In the process, I am building a regression testing framework, a “safety net”. The advantage of this is when we change the backend, I always make sure the tests I have build will surely succeed and in the process the design matures

All the spring frameworks Spring WebFlow being no exception do a good job of this. In this blog I will show you how I do TDD for a simple Spring WebFlow. Those who are new to Spring WebFlow, it is a framework that support data/session management between pages, when you navigate in a workflow within a user session. For example, in the below example, I will talk about the hotel booking application where there is a submit booking, proceed/confirm or cancel booking(2 step webflow). Spring WebFlow helps in managing the session between these 2 steps (pages) without you worrying about session/request data management.

Luckily someone has already built this simple hotel booking application. The way I would build this application is, I will start off with a test as below,

public class BookingFlowExecutionTests extends AbstractXmlFlowExecutionTests {

private BookingService bookingService;

protected void setUp() {
bookingService = EasyMock.createMock(BookingService.class);
}

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

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

public void testStartBookingFlow() {
Booking booking = createTestBooking();

EasyMock.expect(bookingService.createBooking(1L, "keith")).andReturn(
booking);

EasyMock.replay(bookingService);

MutableAttributeMap input = new LocalAttributeMap();
input.put("hotelId", "1");
MockExternalContext context = new MockExternalContext();
context.setCurrentUser("keith");
startFlow(input, context);

assertCurrentStateEquals("enterBookingDetails");
assertResponseWrittenEquals("enterBookingDetails", context);
assertTrue(getRequiredFlowAttribute("booking") instanceof Booking);

EasyMock.verify(bookingService);
}

public void testEnterBookingDetails_Proceed() {
setCurrentState("enterBookingDetails");
getFlowScope().put("booking", createTestBooking());

MockExternalContext context = new MockExternalContext();
context.setEventId("proceed");
resumeFlow(context);

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

public void testReviewBooking_Confirm() {
setCurrentState("reviewBooking");
getFlowScope().put("booking", createTestBooking());
MockExternalContext context = new MockExternalContext();
context.setEventId("confirm");
resumeFlow(context);
assertFlowExecutionEnded();
assertFlowExecutionOutcomeEquals("bookingConfirmed");
}

private Booking createTestBooking() {
Hotel hotel = new Hotel();
hotel.setId(1L);
hotel.setName("Jameson Inn");
User user = new User("keith", "pass", "Keith Donald");
Booking booking = new Booking(hotel, user);
return booking;
}
}

AbstractXmlFlowExecutionTests is an interesting class as a part of spring-webflow.jar which provides all the plumbing for testing the flow and asserting the next steps within a spring webflow. Every test method that tests the flow will start with a “testXXX” method name. To begin with, there are no flow configuration file “src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml”. The test will obviously will fail. As a first step, we need to define the spring flow as below,

<view-state id="enterBookingDetails" model="booking">
<binder>
<binding property="checkinDate" />
<binding property="checkoutDate" />
<binding property="beds" />
<binding property="smoking" />
<binding property="creditCard" />
<binding property="creditCardName" />
<binding property="creditCardExpiryMonth" />
<binding property="creditCardExpiryYear" />
<binding property="amenities" />
</binder>
<on-render>
<render fragments="body" />
</on-render>
<transition on="proceed" to="reviewBooking" />
<transition on="cancel" to="cancel" bind="false" />
</view-state>

<view-state id="reviewBooking" model="booking">
<on-render>
<render fragments="body" />
</on-render>
<transition on="confirm" to="bookingConfirmed">
<evaluate expression="bookingService.persistBooking(booking)" />
</transition>
<transition on="revise" to="enterBookingDetails" />
<transition on="cancel" to="cancel" />
</view-state>

<end-state id="bookingConfirmed"/>

<end-state id="cancel" />

Once you add this configuration in “src/main/webapp/WEB-INF/hotels/booking/booking-flow.xml” file, and run the test again, the test will succeed.

If you notice carefully, transition on=”confirm” will call the business logic on the service layer, in this example, it will call “bookingService.persistBooking(booking)” business logic.

You also see that there are different controller methods like, enterBookingDetails and reviewBooking. These are associated with different view jsp file. But right now they are mocked and we just assert the controllers as in setCurrentState(“enterBookingDetailsreviewBooking”); and assertCurrentStateEquals(“enterBookingDetailsreviewBooking”); in JUnit test.

Now if we have to build views, we will have 2 views jsps, and configure them in view.xml as below,

<definition name="enterBookingDetails" extends="standardLayout">
<put-attribute name="body" value="/WEB-INF/hotels/booking/enterBookingDetails.jsp"/>
</definition>

<definition name="reviewBooking" extends="standardLayout">
<put-attribute name="body" value="/WEB-INF/hotels/booking/reviewBooking.jsp" />
</definition>

You can see the jsp pages in the location as mentioned above, but in the JSP file the key thing that contribute to the webflow is adding,

<form:form modelAttribute="booking" action="${flowExecutionUrl}">

The flowExecutionUrl spring webflow expression language variable will control the flow to next page. There are several such variable within Spring Webflow, for more details click here.

If you want to run this example, you just need to do

mvn tomcat:run

Other References:

Building smoke test harness to test Webservices using SoapUI

We are currently working on a large enterprise integration project. The Integration is developed on a Services Oriented Architecture(SOA), where each application exposes itself as a Webservices. We finished our development cycle and when we migrated the code from development to QA environment, it was very difficult to make sure all the webservices are up and giving a valid response, there were close to 200 services. Mostly the test was done manually.

We went back to the drawing board and analysed how we can automate this. SoapUI, JUnit, Maven and Jenkins came to our rescue. The goals of this implementation is as below,

  • Build SoapUI projects to verify if the services is up and it is responding with meaningful message and we assert the response
  • Parameterizing/externalizing the host name, username and password so that we can run the tests on any environments
  • This can be even tested against live data/production system, hence we should never modify any real data
  • Having a easy web interface to test the interfaces

SoapUI project

  • Create a SoapUI project and Add WSDL (Ctrl U), Create Request and Create a Test Suite, Create Test case and Test Step
  • Bind the webservice method to the Test Step
  • Hardcode the test values to the Test step
  • Assert the response by Adding an assert
  • If the Webservice needs authentication, and if you want to parameterize the user name and password, you need pass ${#Project#username} and ${#Project#password}
  • Finally save the project

JUnit and Maven

You need to create a Maven/Java project and add the below dependency

<dependency>
  <groupId>eviware</groupId>
  <artifactId>maven-soapui-plugin</artifactId>
  <version>4.5.0-SNAPSHOT</version>
</dependency>

In your junit class you need to write the below code,

public void testWebService() {
  SoapUITestCaseRunner runner = new SoapUITestCaseRunner();   runner.setProjectFile(soapUIProjectFile);
  String soapUIProjectFile = System.getProperty("soapUIProjectFile");
  String hostName = System.getProperty("hostName");
  String userName = System.getProperty("userName");
  String password = System.getProperty("password");
  if (hostName != null && hostName.trim().length() > 0) {
    runner.setHost(hostName);
  }
  String[] properties = new String[2];
  properties[0] = (password != null && password.trim().length() > 0) ? "pwd="     + password     : "";
  properties[1] = (userName != null && userName.trim().length() > 0) ? "username="     + userName     : "";
  runner.setProjectProperties(properties);
  runner.run();
  return runner.getFailedTests();
}

Integrating with Jenkins

Create a new Maven Jenkins job, and parameterize the above 4 values as below,

mvn -DsoapUIProjectFile=${soapUIProjectFile} -DhostName=${hostName} -DuserName=${userName} -Dpassword=${password}

2 points to note,

  • Make sure these parameters soapUIProjectFile, hostName, userName, password are created in the Jenkins
  • soapUIProjectFile are checked in the same unit test project and the relative path of the SoupUI project is provided in Jenkins

That is all needs to be done, once it is in place, you just need to run the Jenkins project, it will prompt you with this information, if you pass the values, it will test the webservices in that environment.

We can also enhance this to,

  • Test all the projects in a folder in a recursive way
  • Get meaningful message from junit to inform how many webservices have passed and how many have failed and why they have failed