Showing posts with label junit. Show all posts
Showing posts with label junit. Show all posts

Thursday, March 12, 2015

Selenium JUnit Testing

I recently put together a Selenium JUnit testing project to automate testing for a web project I am working on. Having never done or worked on a Selenium project before I did a little research into finding out how it works and what approach best suits me. The following describes a few things I decided upon.

Selenium is used to automate tests through a web browser. Jenkins was used to run the tests from this project after a successful build of and deploy of the web application.

Useful Resources

Here are a couple of useful links where you can find more information on Selenium:

I decided on the following design principles for the Selenium test project:

Each test should run in its own session

  • Starting a new session means closing the browser and opening it again. The disadvantage to this is that it takes longer to complete a test however the advantage means that any previously run tests don’t pollute the session for the currently running test.
  • The change to the above also means that within Eclipse you can choose to run all tests by running the test suite or you could also selectively choose an individual test to run as each test can be run independently from each other. You could do the same within Buildr or through the command line.

Spring Support

Spring support has been added to the test project classes so that we could take advantage of the following:
  • Dependency injection, example would be connection to the database
  • The ability to run the test cases for different environments based on a spring profile setting
  • Use of Spring support classes, example: jdbctemplate
Important notes to keep in mind:
  • Tests are NOT transactional. Selenium opens a browser and users the applications configured transaction manager.

DbUnit

DbUnit is a JUnit extension targeted at database-driven projects that puts your database into a known state between test runs. DbUnit has the ability to export and import your database data to and from XML datasets.

The tests should not rely on data already in the database since I did not have a dedicated test database and the state of the database is not guaranteed. In certain scenarios we could write tests that use data from the database where we know the data is not going to change. DbUnit is used to populate the database with test data before we run the test and then it removes the data from the database at the end of the test run.

Please refer to the following link in finding out the recommended best practices for DbUnit:
The test project is also using the Spring Test DbUnit project to integrated with the Spring testing framework. It allows you to setup and teardown database tables using simple annotations as well as checking expected table contents once a test completes.

Page Object Model Design Pattern

The Page Object Model is a design pattern to create Object Repository for web UI elements. The following principles apply:
  • Under this model, for each web page in the application there should be corresponding page class
  • This Page class will find the WebElements of that web page and also contains Page methods which perform operations on those WebElements
  • Name of these methods should be given as per the task they are performing i.e., if a loader is waiting for payment gateway to be appear, POM method name can be waitForPaymentScreenDisplay()
Here are some of the advantages of applying the Page Object Model Design Pattern:
  • Page Object Patten says operations and flows in the UI should be separated from verification. This concept makes our code clean and easy to understand
  • Second benefit is the object repository is independent of testcases, so we can use the same object repository for a different purpose with different tools. For example, we can integrate POM with TestNG/JUnit for functional testing and at the same time with JBehave/Cucumber for acceptance testing
  • The number of lines of code are reduced and optimized because of the reusable page methods in the POM classes
  • Methods get more realistic names which can easily be mapped to the operation happening in the UI, i.e. if after clicking on the button we land on the home page, the method name could be 'gotoHomePage()'
Some useful links:

PageFactory and Selenium Support Annotations

The PageFactory class provides a convenient way of initialising the Page Object fields:
  page = PageFactory.initElements(new FirefoxDriver(), TestPage.class);
It can be used to map Page Object properties to fields with matching ids or names. To make it even easier we can do this with the @FindBy annotation:
 @FindBy(id="myFieldId")
 private WebElement myField;
One problem is that every time we call a method on the WebElement the driver will go and find it on the current page again. In an AJAX-heavy application this is what you would like to happen, but in the some cases we know that the element is always going to be there and won't change. We also know that we won't be navigating away from the page and returning. It would be handy if we could "cache" the element once we'd looked it up:
  // The element is now looked up using the name attribute,
  // and we never look it up once it has been used the first time 
  @FindBy(name="myFieldId")
  @CacheLookup
  private WebElement myField;
For more information on the PageFactory please read the following link:

Waiting for an element to exist before looking it up

Selenium tests require a browser to open and a page to load before the code attempts to lookup the expected elements in the page. Selenium has a number of settings to try and cater for this scenario:

Implicit Wait

We can tell Selenium that we would like it to wait for a certain amount of time before throwing an exception when it cannot find the element on the page. Implicit waits will be in place for the entire time the browser is open. This means that any search for elements on the page could take the time the implicit wait is set for.
  WebDriver driver = new FirefoxDriver();
  driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
  driver.get("http://url_that_delays_loading");
  WebElement myDynamicElement = driver.findElement(By.id("myDynamicElement"));
During Implicit wait if the Web Driver cannot find it immediately because of its availability, the WebDriver will wait for the mentioned time and it will not try to find the element again during the specified time period. Once the specified time is over, it will try to search the element once again the last time before throwing exception. The default setting is zero. Once we set a time, the Web Driver waits for the period of the WebDriver object instance.

FluentWait

Each FluentWait instance defines the maximum amount of time to wait for a condition, as well as the frequency with which to check the condition. Furthermore, the user may configure the wait to ignore specific types of exceptions whilst waiting, such as NoSuchElementExceptions when searching for an element on the page.

If you have an element which sometime appears in just 1 second and some time it takes minutes to appear than it is better to use fluent wait, as this will try to find the element again and again until it finds it or until the final timer runs out.
 // Waiting 30 seconds for an element to be present on the page, checking
 // for its presence once every 5 seconds.
 Wait wait = new FluentWait(driver)
   .withTimeout(30, SECONDS)
   .pollingEvery(5, SECONDS)
   .ignoring(NoSuchElementException.class);
 
 WebElement foo = wait.until(new Function() {
   public WebElement apply(WebDriver driver) {
     return driver.findElement(By.id("foo"));
   }
 });
Another case where FluentWait can and is used is when certain events on a page cause the DOM tree to be modified, you can end up with a StaleElementException to the reference you have of that element. When this happens you will need to reinitialise the element or look it up again after the DOM tree has been rebuilt. A StaleElementException is thrown when the element you were interacting is destroyed and then recreated. An example of where this happens in the Meterflow application is on showing a Modal dialog and trying to enter values in input texts to submit a form. Here is an example of code that you can use in this scenario:
 
 public MyPage waitForModalDialogToShow() {
   final Wait<WebDriver> wait = new FluentWait<WebDriver>(driver).withTimeout(30, TimeUnit.SECONDS)
     .pollingEvery(5, TimeUnit.SECONDS)
     .ignoring(NoSuchElementException.class, StaleElementReferenceException.class);
   
   wait.until(new Function<WebDriver, WebElement>() {
     public WebElement apply(WebDriver driver) {
       return driver.findElement(By.id("myModelDialog"));
     }
   });
   
   return this;
 }
In situations where you are using the FindBy annotations on a PO class and not explicitly calling driver.findElement and the DOM was changed due to some user events you may also need to reinitialise the PO object so that the fields are looked up again after the DOM has been recreated. To do this you can call the following static method on the PageFactory class:
 
 /**
  * Reinitialise a PageObject by replacing the fields of an already instantiated Page Object. 
  */
  public void initElements() {
    PageFactory.initElements(driver, this);
  }

Explicit WebDriverWait

The WebDriverWait is a specialization of FluentWait that uses WebDriver instances. It is more extendible in the means that you can set it up to wait for any condition you might like. Usually, you can use some of the prebuilt ExpectedConditions to wait for elements to become clickable, visible, invisible, etc.

There can be an instance when a particular element takes more than a minute to load. In that case you don't want to set a huge time to Implicit wait because then your browser will wait the same time for every element. To avoid that situation you can put a separate time on the required element.
  WebDriverWait wait = new WebDriverWait(driver, 10);
  WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));

Selenium Locator Strategies

There are 8 locators that Selenium’s commands support:
  1. id - Ids are the most preferred way to locate elements on a page, fast and reliable way to locate elements
  2. name - An efficient way to locate an element but unlike Ids, name attributes don’t have to be unique in a page
  3. identifier - Combination of id and name, first checks the @id attribute and if no match is found it tries the @name attribute
  4. css - Locate an element by using CSS selectors to find the element in the page
  5. xpath - Locate an element using an XPath query
  6. link - Locate a link element ("a" tag) by the text used within the link tag
  7. dom - Locate elements that match the JavaScript expression referring to an element in the DOM of the page
  8. ui - Selenium IDE extension (http://ttwhy.org/code/ui-doc.html)

FindBy annotation support

The @FindBy annotation supports the following locators:
id
 My text
 
 @FindBy(id="elementId")
 private WebElement myElement;
name
 
 My text
 @FindBy(name="elementName")
 private WebElement myElement;
className
  
block
 
 @FindBy(className="element-css")
 private WebElement myElement;
css
 
block
block
Google
 @FindBy(css="div.element-css")
 private WebElement myElementEx1;

 @FindBy(css="div.element-css[id='myElementId']")
 private WebElement myElementEx2;

 @FindBy(css="input[name='textName'][type='text']")
 private WebElement myElementEx3;

 @FindBy(css="a[name='link']")  
 private WebElement myElementEx4;
linkText
 Google
 @FindBy(linkText="Google")
 private WebElement myElement;
partialLinkText
 Google
 @FindBy(partialLinkText="Goo")
 private WebElement myElement;
tagName
 
 Link1
 
 Link2

 Link3
 
 @FindBy(tagName = "a")
 private List myLinks;
xpath
  
block
 
 @FindBy(xpath="//span[@class='element-css']")
 private WebElement myElement;

A few examples

  • Finding a cell in a table generated by Primefaces, an example of what the generated html table would look like:
 <div id="myFormId:myTableId" class="ui-datatable ui-widget">
  <div class="ui-datatable-tablewrapper">
    <table role="grid">
      <thead id="myFormId:myTableId_head">
        <tr role="row">
          <th id="myFormId:myTableId:j_idt44" class="ui-state-default" role="columnheader">
            <span class="ui-column-title">Column 1</span>
          </th>
          <th id="myFormId:myTableId:j_idt45" class="ui-state-default" role="columnheader">
            <span class="ui-column-title">Column 2</span>
          </th>
        </tr>
      </thead>
      <tfoot id="myFormId:myTableId_foot"/>
      <tbody id="myFormId:myTableId_data" class="ui-datatable-data ui-widget-content">
        <tr class="ui-widget-content ui-datatable-even ui-datatable-selectable" role="row">
          <td role="gridcell">
            <span id="myFormId:myTableId:0:col1">Row 1 - Value of column 1</span>
          </td>
          <td role="gridcell">
            <span id="myFormId:myTableId:0:col2">Row 1 - Value of column 2</span>
          </td>
        </tr>
        <tr class="ui-widget-content ui-datatable-even ui-datatable-selectable" role="row">
          <td role="gridcell">
            <span id="myFormId:myTableId:2:col1">Row 2 - Value of column 1</span>
          </td>
          <td role="gridcell">
            <span id="myFormId:myTableId:2:col2">Row 2 - Value of column 2</span>
          </td>
        </tr>
      </tbody>
    </table>
  </div>
 </div>
 // find all the table cells that match the css selector
 @FindBy(css="div[id='myFormId:myTableId'] > div > table > tbody > tr > td > span")

 // same as above but won't only check the next element in tree and will keep searching until a match is found
 @FindBy(css="div[id='myFormId:myTableId'] table td span")

 // find all the table cells that match the xpath query
 @FindBy(xpath="//div[@id='myFormId:myTableId']/div/table/tbody/tr/td/span")

 // same as above but won't only check the next element in tree and will keep searching until a match is found
 @FindBy(xpath="//div[@id='myFormId:myTableId']//table//td//span")

 // to find a cell that contains text
 @FindBy(xpath="//div[@id='myFormId:myTableId']//table//td//span[contains(text(),'Row 1 - Value of column 1')]")
  • Find an element whose ID matches part of an expression, the following examples use an "a" link element but can be any valid html tag:
 <a id="j_idt38:myLink" href="/tmp.xhtml" class="ui-link ui-widget">Tmp</a>
 // css strategy to find an element whose ID starts with 'j_idt38'
 @FindBy(css="a[id^='j_idt38']")

 // css strategy to find an element whose ID ends with 'myLink'
 @FindBy(css="a[id$='myLink']")

 // css strategy to find an element whose ID contains 'myLink'
 @FindBy(css="a[id*='myLink']")

 // xpath query strategy to find an element whose ID contains 'myLink'
 @FindBy(xpath="//a[contains(@id, 'myLink')]")
  • Perform Javascript actions, the following example describes a javascript event being carried out when the mouse hovers over certain menu items:
 <li id="topNavFrm:adminSubmenu">
  <a href="#">
    <span />
    <span >Administration</span>
    <span />
  </a>
  <ul role="menu">
    <li id="topNavFrm:usersSubMenu">
      <a href="#">
        <span />
        <span>Users</span>
        <span />
      </a>
      <ul>
        <li>
          <a id="topNavFrm:userGroupMenuItem" href="/users/groups.xhtml">
            <span>User Groups</span>
          </a>
        </li>
        <li>
          <a href="/users/users.xhtml">
            <span>Users</span>
          </a>
        </li>
      </ul>
    </li>
  </ul>
 </li>
 @FindBy(id="topNavFrm:adminSubmenu")
 private WebElement adminMenu;

 public UsersAdminPage mouseOverUsersDetailMenu() {
   Actions action = new Actions(driver);
   action.moveToElement(adminMenu).perform();
       
   WebElement usersSubElement = adminMenu.findElement(By.cssSelector("li[id='topNavFrm:usersSubMenu'] a"));
   action.moveToElement(usersSubElement);
       
   WebElement usersAdminSubElement = adminMenu.findElement(By.id("topNavFrm:usersMenuItem"));
   action.moveToElement(usersAdminSubElement);
       
   action.click();
   action.perform();
  
   return this;
 }

XPath Query Testing

Testing XPath queries can be done within the browser, this sections shows examples of how you could do it with some of them:

Chrome

To type in an xpath to search a page:
  • Press F12 to open Chrome Developer Tool
  • In "Elements" panel, press Ctrl+F
  • In the search box, type in XPath or CSS Selector, if elements are found, they will be highlighted in yellow.
To copy an xpath from an element in the page (same can be done to retrieve CSS path):
  • Right click on element and select Inspect Element
  • In elements view right click on element line and select Copy XPath

Firefox

To type in an xpath to search a page:
  • Install Firebug
  • Install Firepath
  • Press F12 to open Firebug
  • Switch to FirePath panel
  • In dropdown, select XPathor CSS
  • Type in to locate
To copy an xpath from an element in the page:
  • Click on Inspect element button and place your tip of cursor on any element for which you want to find XPath
  • Right Click on highlighted code and Select Copy XPath

Tuesday, August 4, 2009

Shopping Cart Web Application - Part 9 - Advanced Test Cases

Introduction
Our application has grown in size and complexity since the last time we created our unit test for the shopping service class. This is what I would like to do:
  • Integrate our test case with Spring – this is pretty cool, instead of creating and initializing our objects manually or performing any JNDI looking ups ourselves (that is if we did need to do that) we will let Spring take care of this for us using our existing Spring bean configuration files we created in the previous tutorial.
  • Mock objects – In some cases it is very difficult to test a service. In our case it isn’t because all we are doing is creating an instance of the service ourselves but what if the instance of the service existed on a remote server and you needed to use a remote call to lookup that instance? It adds additional overhead to your test case. For one it relies on this remote service to be available at the time the test is run and two it requires additional resources in order to perform this lookup. An alternative to testing the service is to create a mock of the service and predict the outcome of the test. So you don’t actually test the service implementation but your very own mock that is based on the service interface. This sounds like a lot of work but it isn’t actually thanks to a third party library we are going to use called EasyMock.
  • Database integration testing – We touch on an integration test for testing transactions with the database. We will be using spring as well as a third party library called DBUnit.

What you need before we get started
  • ShoppingCartAdvancedTestCasesPart9 sample application. - This is an Eclipse archive file that contains one Eclipse project. Please import this project into your Eclipse environment.
  • You will need the previous third party libraries that were mentioned and used in earlier parts to this tutorial
  • The following libraries need to be added to your existing Libs project:

What we will cover
  • Spring integration
  • Mock object tests
  • Database integration tests

Level
  • Intermediary

Spring Integration
We are going to use Spring for two purposes, the first is for our Shopping service class. We will let Spring manage the creation and initialization of this object for us so. We will also make use of our existing spring bean configuration file to initialize our service class. Secondly we are going to use Spring to create test data for us so that we can use it in our test case. To do this we will create a Spring bean configuration file specifically for our tests. I have put it under our test folder and called it shoppingcart-test.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
  <bean id="shoppingService" class="com.mydomain.shoppingcart.service.impl.ShoppingManager">
    <property name="itemDao">
      <ref bean="itemDao" />
    </property>
    <property name="basketDao">
      <ref bean="basketDao" />
    </property>
  </bean>
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="shoppingCartDataSource" />
  </bean>
  <import resource="classpath:shoppingcart-database.xml" />
</beans>


There are two different beans defined here, namely:
  • shoppingService – our shopping service class
  • jdbcTemplate – spring class used to simplify the use of JDBC

Next we will make changes to our ShoppingServiceTest class.

package com.mydomain.shoppingcart.service.test;

import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.LinkedList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.mydomain.shoppingcart.bo.Basket;
import com.mydomain.shoppingcart.bo.BasketItem;
import com.mydomain.shoppingcart.bo.Item;
import com.mydomain.shoppingcart.dao.BasketDao;
import com.mydomain.shoppingcart.service.ShoppingService;

/**
* @author Ross
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/shoppingcart-test.xml" })
public class ShoppingServiceTest {
  private Basket basket;
  private BasketDao basketDaoMock;
  @Autowired
  private ShoppingService shoppingManager;
  private ShoppingService shoppingManagerMock;
  private Item testItem;

 /**
  * Tests adding an item to the basket.
  */
  @Test
  @DirtiesContext
  public void addItem() {
    int itemCount = basket.getItemCount();
    basket.addItem(testItem);
    assertEquals(itemCount + 1, basket.getItemCount());
  }

 /**
  * Tests emptying the basket.
  */
  @Test
  @DirtiesContext
  public void empty() {
    basket.empty();
    assertEquals(0, basket.getItemCount());
  }

 /**
  * Tests finding items.
  */
  @Test
  public void findItems() {
    try {
      int itemCount = basket.getItemCount();
      List<Item> mockResult = new LinkedList<Item>();
      for (int i = 0; i < itemCount; i++) {
        mockResult.add(new Item());
      }
      expect(shoppingManagerMock.findItems()).andReturn(mockResult);
      replay(shoppingManagerMock);

      List<Item> allItems = new LinkedList<Item>(shoppingManagerMock.findItems());
      assertEquals(itemCount, allItems.size());

      verify(shoppingManagerMock);
    } catch (Exception e) {
      e.printStackTrace();
      fail("Error in Shopping Manager");
    }
  }

 /**
  * Tests removing an item from the cart.
  */
  @Test
  @DirtiesContext
  public void removeItem() {
    int itemCount = basket.getItemCount();
    for (BasketItem basketItem : basket.getBasketItems()) {
      basket.removeItem(basketItem.getItem());
      break;
    }
    assertEquals(itemCount - 1, basket.getItemCount());
  }

 /**
  * Tests saving a basket.
  */
  @Test
  public void saveBasket() {
    try {
      shoppingManager.setBasketDao(basketDaoMock);
      basketDaoMock.saveOrUpdateBasket(basket);
      replay(basketDaoMock);
      shoppingManager.updateBasket(basket);
      verify(basketDaoMock);
    } catch (Exception e) {
      e.printStackTrace();
      fail("Error in Shopping Manager");
    }
  }

 /**
  * Sets up the test fixture.
  *
  * Called before every test case method.
  */
  @Before
  public void setUp() {
    try {
      shoppingManagerMock = createMock(ShoppingService.class);
      basketDaoMock = createMock(BasketDao.class);
      testItem = new Item(1l, "Candy Cotton", "Candy coated milky tarts", 8.50d);
      basket = new Basket();
      basket.addItem(new Item(2l, "Jelly Beans", "Jelly icecream waffle cream", 18.99d));
      basket.addItem(new Item(3l, "Jam Doughnut", "Strawberry jam and Christmas pudding", 23.00d));
    } catch (Exception e) {
      e.printStackTrace();
      fail("Error setting up test case");
    }
  }
}

Ok there are a couple of things I would like to point out here and step through them each:
  • @RunWith attribute is a JUnit annotation and is used when you want to override the default JUnit runner. Here we want to use Springs own implementation in order to use Spring in our tests.
  • @ContextConfiguration is a Spring annotation and sets the location of the Spring bean configuration files needed for this test.
  • @Autowired is a Spring annotation that tells the compiler to automatically "wire" the variable (in this case shoppingManager) to the matching bean configured in the Spring bean configuration file.
  • Lastly we have our test method. Nothing really fancy about the body of the method. You notice we didn't have to create any objects or initialize anything as Spring did that for us. There is one more additional Spring annotation, @DirtiesContext. This annotation ensures that this method gets a clean context before it executes. Another test method may have altered the state of the beans defined in the Spring configuration files this annotation ensures the state of those beans will be the same as its original state before it executes.

Mock object test
Mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A mock object is created to test the behavior of some other object. Mock objects can simulate the behavior of complex, real (non-mock) objects and are therefore useful when a real object is impractical or impossible to incorporate into a unit test. If an object has any of the following characteristics, it may be useful to use a mock object in its place:
  • Supplies non-deterministic results (e.g. the current time or the current temperature)
  • Has states that are difficult to create or reproduce (e.g. a network error)
  • Is slow (e.g. a complete database, which would have to be initialized before the test)
  • Does not yet exist or may change behavior
  • Would have to include information and methods exclusively for testing purposes (and not for its actual task)
  • Mock objects have the same interface as the real objects they mimic, allowing a client object to remain unaware of whether it is using a real object or a mock object.

EasyMock
EasyMock is a third party library that provides Mock Objects for interfaces in JUnit tests by generating them on the fly.

Shopping Cart Application
In the shopping cart application I created two different mock objects to show you how and where you can use them:
  • ShoppingManagerMock – A mock for the shopping service interface. You would most likely want to create a mock object for a service that you are looking up remotely and have no control over it. In the shopping cart application this is not the case but this example demonstrates what you can do.
  • BasketDaoMock – A mock for the basket dao. You may not want to test your service class but not the actual persistence of data to the database. Simply because there is a performance overhead setting up connections and persisting data to the database as well as managing the test data that you save and delete from the database. You don't want to corrupt your database with your tests. The last part of this tutorial shows you an example of testing your DAO's.

In our setup method we create the mock objects using EasyMocks createMock static method. This call creates a mock object that implements the given interface.

The first thing we do is define what method calls we expect should be made on our mock object. We can also define what result we expect back from a method call:

expect(shoppingManagerMock.findItems()).andReturn(mockResult); 

Once we have set up all our expectations (in our case only one) we change the mode of our mock test to replay. What this means is that every other method call made on our mock object from this point on will be checked to see if it is what we expect to be called.

replay(shoppingManagerMock); List<Item> allItems = new LinkedList<Item>(shoppingManagerMock.findItems()); assertEquals(itemCount, allItems.size());

Finally at the end of our test we call the EasyMock verify method that verifies the expected behaviour and throws an exception if it does not match.

verify(shoppingManagerMock); 

I created a new test method to test the BasketDao mock object. As you can see it is very similar to the above test. You will notice that I am not using the shopping manager mock object.

@Test
public void saveBasket() {
  try {
    shoppingManager.setBasketDao(basketDaoMock);
    basketDaoMock.saveOrUpdateBasket(basket);
    replay(basketDaoMock);
    shoppingManager.updateBasket(basket);
    verify(basketDaoMock);
  } catch (Exception e) {
    e.printStackTrace();
    fail("Error in Shopping Manager");
  }
}

Database Integration Tests
I would call this a type of integration test because we are testing the integration of our application with the database. I created a new test called BasketDaoTest to test the BasketDao data access class. These are the following steps I took when putting together this test:
  • Create test data that we will use to insert into our database and then remove at the end of the test
  • Create new test class to test the BasketDao class

1. Test data
We are going to create test data to run our tests against. You don’t want to run your test against data already in the database. For one you don’t know what data is there and two you don’t want to mess with someone else’s data. It is therefore essential to create our own data.
I created an xml file (shoppingcart-dbunit.xml) for my test data that will be used by DBUnit to insert into the database and at the end of the test remove it from the database. An example of the xml I used looks like the following:

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
  <basket ID="100001" />
  <item ID="100001" DESCRIPTION="Candy coated milky tarts" NAME="Candy Cotton" PRICE="8.5"/>
  <item ID="100002" DESCRIPTION="Jelly icecream waffle cream" NAME="Jelly Beans" PRICE="18.99"/>
  <basket_item ID="100001" BASKET_ID="100001" ITEMS_ID="100001" QUANTITY="2" PRICE="17"/>
  <basket_item ID="100002" BASKET_ID="100001" ITEMS_ID="100002" QUANTITY="1" PRICE="18.99"/>
</dataset>

DbUnit is a JUnit extension targeted at database-driven projects that puts your database into a known state between test runs. The easiest way to create this test data is to export the data from the database. There are specific DBUnit classes you can use to export data in the format required either by writing a java application that does it or by using an ant task made available by DBUnit to do this.


2. Test class
Now that we the data that our test is going to use we can move right along to our test class.
Once again we will leverage off Spring to make our lives a whole lot easier. If you take a look at our class declaration you will notice a couple of interesting points.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/shoppingcart-test.xml" })
@TransactionConfiguration
@Transactional
public class BasketDaoTest extends AbstractTransactionalJUnit4SpringContextTests {

This class is an abstract transactional extension of AbstractJUnit4SpringContextTests () which adds convenience functionality for JDBC access. It expects a DataSource bean and a PlatformTransactionManager bean to be defined in the Spring application context.

This class exposes a SimpleJdbcTemplate and provides an easy way to count the number of rows in a table, delete from the database , and execute SQL scripts within a transaction.

The @TransactionConfiguration and @Transactional annotations configure transactions for the tests.

I have broken our test class up into the following steps:
  1. Record current state of data in the database
  2. Insert test data into the database
  3. Run test on data in database
  4. Delete test data from database
  5. Validate data stored in the database to match original state

1. Record current state of data in the database
The first step I decided to do is to determine how many rows there are for the basket table. I did this so that at the end of the test I can compare the number of rows in the basket table to this value and check to see whether or not they are equal.

@BeforeTransaction
public void beforeTransaction() {
  basketTableRowCount = countRowsInTable(BASKET_TABLE_NAME);
}

The method with the annotation @BeforeTransaction is run before the transaction starts.

2. Insert test data into the database
I take the test data we created and I use DBUnit classes to insert the data into the database.

@Before
public void setUpTestDataWithinTransaction() {
  try {
    IDatabaseConnection connection = new DatabaseConnection(jdbcTemplate.getDataSource().getConnection());
    DatabaseOperation.INSERT.execute(connection, new FlatXmlDataSet(new FileInputStream(TEST_DATA_FILE)));
    jdbcTemplate.getDataSource().getConnection().close();
  } catch (Exception e) {
    e.printStackTrace();
  }
}

As we saw in the earlier tutorial on test cases the method with a @Before annotation gets called before any test methods.

3. Run test on data in database
All our JUnit test methods can now be run

@Test
@Rollback(true)
public void saveOrUpdateBasket() {
  assertNotNull("Basket DAO is null.", basketDao);
  try {
    Collection<Basket> baskets = basketDao.loadById(1);

    assertNotNull("Basket list is null.", baskets);
    assertEquals("Number of baskets should be " + basketTableRowCount + 1 + ".", basketTableRowCount + 1, baskets.size());

    for (Basket basket : baskets) {
      assertNotNull("Basket is null.", basket);
    }
  } catch (Exception e) {
    fail("Error in saveOrUpdateBasket");
  }
}

The @Test annotation identifies this method as our test method to run. The @Rollback annotation is a Spring test annotation used to indicate whether or not the transaction for the annotated test method should be rolled back after the test method has completed. If true, the transaction will be rolled back; otherwise, the transaction will be committed.

4. Delete test data from database
At the end of all our tests we use DBUnit again to delete our test data from the database so that the state of the database returns to what it was before the start of our test case.

@After
public void tearDownWithinTransaction() {
  try {
    IDatabaseConnection connection = new DatabaseConnection(jdbcTemplate.getDataSource().getConnection());
    DatabaseOperation.DELETE.execute(connection, new FlatXmlDataSet(new FileInputStream(TEST_DATA_FILE)));
    jdbcTemplate.getDataSource().getConnection().close();
  } catch (Exception e) {
    fail("Error in tearDownWithinTransaction");
  }
}

As we saw in the earlier tutorial on test cases the method with a @After annotation gets called after all the test methods have been called.

5. Validate data stored in the database to match original state
Here we validate the number of rows in our basket table to the number we originally retrieved at the start of our test.

@AfterTransaction
public void afterTransaction() {
  assertEquals(basketTableRowCount, countRowsInTable(BASKET_TABLE_NAME));
}

The method with the annotation @AfterTransaction is run after the transaction ends.

Monday, May 11, 2009

Shopping Cart Web Application - Test cases - Part 3

Introduction
Software testing is a very important aspect to any software development lifecycle. It provides stakeholders with information about the quality of the product that is being tested.


What you need before we get started
  • You will need to install the Sun JDK on your local pc in order to compile the projects code. The version I used was JDK1.6.0_07. I recommend using this version or the latest in case you run into any problems with the different versions that are available.
  • I use Eclipse as my IDE to develop in. You don’t need Eclipse if you are just going to read this tutorial but if you intend to test the application then I would recommend it.
  • You can import the project I created for this tutorial into Eclipse and run the unit test case.
  • You will need the following third party libraries in order to run the test:


What we will cover
  • What is unit testing?
  • Code generation
  • Import generated source into Eclipse
  • JUnit
  • Create the unit test
  • Annotations
  • Running the test


Level
  • Beginner


Testing
There are a number of different software testing methods, namely:
  • Unit testing - tests the minimal software component, or module. Each unit (basic component) of the software is tested to verify that the detailed design for the unit has been correctly implemented.
  • Integration testing - exposes defects in the interfaces and interaction between integrated components. Progressively larger groups of tested software components corresponding to elements of the architectural design are integrated and tested until the software works as a system.
  • System testing - tests a completely integrated system to verify that it meets its requirements.
  • System integration testing - verifies that a system is integrated to any external or third party systems defined in the system requirements.

This tutorial will only focus on unit testing. Unit testing is a software design and development method where the programmer verifies that individual units of source code are working properly. Unit testing is a simple and effective process that improves delivery, quality and flexibility of a project. Having unit tests makes it easier and safer to modify the code because the tests document and protect the intended behavior and will instantly catch any regressions. A unit is the smallest testable part of an application

The goal of unit testing is to isolate each part of the program and show that the individual parts are correct. A unit test provides a strict, written contract that the piece of code must satisfy. As a result, it affords several benefits. Unit tests find problems early in the development cycle.

The goal for this tutorial is to show you how to write a unit test for a particular class using a third party library called JUnit.


Code Generation
I was able to take the class diagram we created in the previous exercise and generate Java source from it using ArgoUML. I used the generated source as my base project to work from and modified the code slightly. I also created a unit test class. Please download the file ShoppingCartTestCasesPart3.zip that contains the shopping-cart-core-test-cases-part3 Eclipse project I created for this tutorial.


Import existing project into Eclipse
You can follow these steps to import the shopping-cart-core-test-cases-part3 project into Eclipse:
  • Open Eclipse
  • Select File from the top navigation menu
  • Select Import
  • Select Existing Projects into Workspace under the General folder
  • Select the Select archive file radio button
  • Browse to where you saved the ShoppingCartTestCasesPart3.zip file
  • Make sure the shopping-cart-core-test-cases-part3 project is selected
  • Click the Finish button

The shopping-cart-core-test-cases-part3 project should be imported into your Eclipse workspace. The project may not compile because it may require a library that isn't part of the project. The next section talks a little about configuring your project so that it is pointing to the required libraries.


JUnit
I have chosen to use JUnit as a framework to build my tests. JUnit is a unit testing framework for the Java programming language. I added the JUnit library we are going to need for our test case to our project. How did I do this you ask? I chose to create a simple project called LIBS where I will store all my libraries inside. I copied the JUnit library to the LIBS project. I than included the library in the projects classpath by right clicking on the project -> Properties -> Java Build Path -> Libraries -> Add JARs -> LIBS -> JUnit4.5.


Create the unit test
1. Create test source folder
I created a new source folder called test where all my test classes will go I also added it as a src folder by right clicking on the test folder -> Build Path -> Source -> Use as Source Folder.

2. Create test class
I created a package called com.mydomain.shoppingcart.service.test under the new test folder. And within this package I created the ShoppingServiceTest class.

ShoppingServiceTest.java


package com.mydomain.shoppingcart.service.test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.util.ArrayList;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import com.mydomain.shoppingcart.bo.Basket;
import com.mydomain.shoppingcart.bo.Item;
import com.mydomain.shoppingcart.service.ShoppingService;
import com.mydomain.shoppingcart.service.impl.ShoppingManager;

/**
* @author Ross
*/
public class ShoppingServiceTest {
private Basket basket;
private ShoppingService shoppingManager;
private Item testItem;
private int itemsCount;

/**
* Tests adding an item to the basket.
*/
@Test
public void addItem() {
int itemCount = basket.getItemCount();
basket.addItem(testItem);
double expectedBalance = 0;
for (Item item : basket.getItems()) {
expectedBalance = expectedBalance + item.getPrice();
}
assertEquals(expectedBalance, basket.getBalance(), 0.0);
assertEquals(itemCount + 1, basket.getItemCount());
}

/**
* Tests emptying the basket.
*/
@Test
public void empty() {
basket.empty();
assertEquals(0, basket.getItemCount());
}

/**
* Tests finding items.
*/
@Test
public void findItems() {
try {
List<Item> allItems = new ArrayList<Item>(shoppingManager.findItems());
assertEquals(itemsCount, allItems.size());
} catch (Exception e) {
e.printStackTrace();
fail("Error in Shopping Manager");
}
}

/**
* Tests removing an item from the cart.
*/
@Test
public void removeItem() {
int itemCount = basket.getItemCount();
for (Item item : basket.getItems()) {
basket.removeItem(item);
break;
}
assertEquals(itemCount - 1, basket.getItemCount());
}

/**
* Sets up the test fixture.
*
* Called before every test case method.
*/
@Before
public void setUp() {
shoppingManager = new ShoppingManager();
itemsCount = shoppingManager.findItems().size();
testItem = new Item(1l, "Candy Cotton", "Candy coated milky tarts", 8.50d);
basket = new Basket();
basket.addItem(new Item(2l, "Jelly Beans", "Jelly icecream waffle cream", 18.99d));
basket.addItem(new Item(3l, "Jam Doughnut", "Strawberry jam and Christmas pudding", 23.00d));
}
}

Annotations
JUnit4 uses annotations to define which methods should be tested. The @Test line in the code is an example of an annotation. It is used to identify which methods should be treated as test methods. A test method will be executed when your JUnit tests are run.

Many APIs require a fair amount of boilerplate code (any code that is or can be reused in new contexts or applications without being changed much from the original). For example, in order to write a JAX-RPC web service, you must provide a paired interface and implementation. This boilerplate could be generated automatically by a tool if the program were “decorated” with annotations indicating which methods were remotely accessible.

Annotations are like meta-tags that you can add to your code and apply them to package declarations, type declarations, constructors, methods, fields, parameters, and variables. As a result, you will have helpful ways to indicate whether your methods are dependent on other methods, whether they are incomplete, whether your classes have references to other classes, and so on.

Annotation-based development relieves Java developers from the pain of cumbersome configuration. Annotation-based development lets us avoid writing boilerplate code under many circumstances by enabling tools to generate it from annotations in the source code. This leads to a declarative programming style where the programmer says what should be done and tools emit the code to do it.

Looking at our ShoppingServiceTest class we have a number of methods. The first is the setUp method. This method has a JUnit4 annotation defined above it called @Before. This method gets called before any test methods. It is used to initialize any global variables used within the test methods.

The following test methods are defined in our class:
  • empty() - Empty a basket and check whether or not the basket has any items
  • addItem() - Add an Item to the Basket then check whether or not the Item was added
  • removeItem() - Remove an item from the basket then check whether or not the Item was removed
  • findItems() - Lookup a list of all the items and compare the number of items retrieved with the actual number of items


Running the test
We will run our tests within Eclipse to do this right click on our test class ShoppingServiceTest.java -> Run as -> JUnit test.

JUnit should run and you should be able to see the results of your test in the JUnit View tab.
That is all on JUnit testing for now. I would strongly encourage you to write other tests and run them to really get comfortable with this framework. Until the next tutorial, happy testing!