Unit tests for validator (design and run them from GUI)

In the context of the Gazelle X Validator project, we thought about designing and running the unit tests for rules from the GUI. That means that the user who will create its validators and rules using the tool will also be able to design the associate test cases. 

In order to use this approach in some other tools (for instance of the validation of Audit messages), we decided to externalize the common part of this feature in a new module called gazelle-validator-unit-testing.

Sources

https://scm.gforge.inria.fr/svn/gazelle/Maven/gazelle-validator-unit-testing/trunk

Jenkins' Job

http://gazelle.ihe.net/jenkins/job/gazelle-validator-unit-testing/

Jira project

http://gazelle.ihe.net/jira/browse/VUT

Maven metadata

<dependency>
  <groupId>net.ihe.gazelle</groupId>
  <artifactId>gazelle-validator-unit-testing-ejb</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <type>ejb</type>
</dependency>
<dependency>
  <groupId>net.ihe.gazelle</groupId>
  <artifactId>gazelle-validator-unit-testing-war</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <type>war</type>
</dependency>

Module content

This module has been designed to create and run unit tests cases. Basically, the need is to check the correctness of a validator designed thanks to a Graphical User Interface without writing Java code.

An abstract unit test case is not linked to any object. You will have to extends this class to link the test cases to the entity you want to check. Then, each test case is defined by

  • a keyword
  • an expected result
  • a list of files to use as inputs (those files are stored on the file system and referenced in the database - see UnitTestFile entity)
  • the tested entity

Unit test list

The result of each run is stored in database using the UnitTestLog entity which contains the following attributes (extract):

  • run test cases
  • used files
  • tested version of the entity
  • effective result
  • timestamp
  • reason for failure

Unit test logs

How to use it

Configure your project

In the dependency of your project, add references to the EJB and WAR modules of the gazelle-validator-unit-testing project (see Maven metadata above). Then in the pom.xml file of the EAR module add (in the section dedicated to the configuration of the maven-ear-plugin):

<ejbModule>
    <groupId>net.ihe.gazelle</groupId>
    <artifactId>gazelle-validator-unit-testing-ejb</artifactId>
    <uri>gazelle-validator-unit-testing-ejb.jar</uri>
</ejbModule>

 In EJB/src/main/resources/META-INF/persistence.xml add a reference to the new EJB module

<jar-file>gazelle-validator-unit-testing-ejb.jar</jar-file>

In the pom.xml file of the WAR module, add a dependency to gazelle-validator-unit-testing-war.

Extend UnitTest abstract entity

The UnitTest abstract class defines common attributes shared by the several type of unit tests you might want to define. The SINGLE_TABLE inheritance strategy is used so you need to define a descriminator (@DescriminatorValue) to distinguish the various objects which will be created in the ut_unit_test table.

Below is an example extracted from gazelle-x-validation-ejb module.

@Entity
@DiscriminatorValue("XVAL_RULE_UT")
public class RuleUnitTest extends UnitTest implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 904725100611305807L;
	
	@ManyToOne(targetEntity=Rule.class)
	@JoinColumn(name="tested_rule_id")
	private Rule testedRule;
	
	
	public RuleUnitTest(){
		super();
	}
	
	public RuleUnitTest(Rule rule){
		super();
		this.testedRule = rule;
		setKeyword(TEST_PREFIX + rule.getKeyword() + SEPARATOR);
		setLastResult(null);
	}
    // ...
}

If you need extract attributes, feel free to add them in your child class, you will then be able to add them in the GUI.

Graphical User Interface

Currently, three pages are pre-defined:

  • Browse unit test cases
  • Browse test logs
  • Edit unit test cases

Abstract classes for the backend beans and XHTML templates have been defined and are available in the module.

Manage unit tests

A stateless bean is used to perform some operations on unit test. Extends the net.ihe.gazelle.validator.ut.action.UnitTestManager<T extends UnitTest> class to make those operations available in your tool.

Example of use (from gazelle-x-validation-ejb)

@Name("ruleUnitTestManager")
@Scope(ScopeType.STATELESS)
public class RuleUnitTestManager extends UnitTestManager<RuleUnitTest> {

	/**
	 * 
	 */
	private static final long serialVersionUID = -3258729114956565360L;

	public void executeAllTests(Rule rule) {
		List<RuleUnitTest> unitTests = RuleUnitTestDAO.instanceWithDefaultEntityManager().getUnitTestsForRule(rule);
		executeList(unitTests);
	}

	public String createNewTest(Rule rule) {
		return XValidationPages.ADMIN_RULE_UNIT_TEST.getSeamLink() + "?rule=" + rule.getKeyword();
	}

	public String displayUnitTest(RuleUnitTest unitTest) {
		return XValidationPages.ADMIN_RULE_UNIT_TEST.getSeamLink() + "?unitTest=" + unitTest.getKeyword();
	}
    //...
}

Browse unit tests

Abstract class: net.ihe.gazelle.validator.ut.action.UnitTestBrowser<T extends UnitTest, Q extends UnitTestAttributes<T>>: for the basic features provided by the template, you only need to implement the abstract methods declared in this class. Use a PAGE scope.

XHTML template: /unittesting/browseUnitTestsTemplate.xhtml

The following parameters are expected:

  • managedBean : reference to the bean which extends UnitTestBrowser, eg. #{ruleUnitTestBrowser}
  • unitTestManagerBean: reference to the bean which extends UnitTestManager, eg. #{ruleUnitTestManager}

In addition, you can add:

  • filters in the search criteria panel : moreFilters (<ui:define name="moreFilters">your components here</ui:define>)
  • columns to the table gathering the unit tests: moreColumns

Browse unit test logs

Abstract class: net.ihe.gazelle.validator.ut.action.UnitTestLogBrowser: for the basis features provided by the template, you only need to implement the abstract methods declated in the class. Use a PAGE scope.

XHTML template: /unittesting/unitTestLogsTemplate.xhtml

The following parameters are expected:

  • managedBean : reference to the bean which extends UnitTestLogBrowser, eg. #{ruleUnitTestLogBrowser}
  • utManagerBean: reference to the bean which extends UnitTestManager, eg. #{ruleUnitTestManager}

In addition, you can add:

  • filters in the search criteria panel : moreFilters (<ui:define name="moreFilters">your components here</ui:define>)
  • columns to the table gathering the logs: moreColumns

 

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
	xmlns:s="http://jboss.com/products/seam/taglib"
	xmlns:ui="http://java.sun.com/jsf/facelets"
	xmlns:f="http://java.sun.com/jsf/core"
	xmlns:h="http://java.sun.com/jsf/html"
	xmlns:a4j="http://richfaces.org/a4j"
	xmlns:rich="http://richfaces.org/rich"
	xmlns:g="http://www.ihe.net/gazelle" template="/layout/template.xhtml">

	<ui:param name="pageName" value="Unit test logs" />
	<ui:param name="pageNameUrl"
		value="/xvalidation/testing/ruleUnitTestEditor.seam" />
	<ui:param name="pageNameTitle" value="Unit test logs" />

	<ui:define name="header">
		<a4j:loadStyle src="/stylesheet/unittest-style.css" /> <!--imports specific CSS classes used by the template-->
	</ui:define>

	<ui:define name="body">
		<s:decorate template="/unittesting/unitTestLogsTemplate.xhtml">
			<ui:param name="managedBean" value="#{ruleUnitTestLogBrowser}" />
			<ui:param name="utManagerBean" value="#{ruleUnitTestManager}" />
			<ui:define name="moreFilters">
				<ui:include src="/filter/filterSuggest.xhtml">
					<ui:param name="filterWidth" value="240" />
					<ui:param name="filter" value="#{ruleUnitTestLogBrowser.filter}" />
					<ui:param name="filterId" value="validator" />
					<ui:param name="filterName" value="Tested validator" />
					<ui:param name="filterForm" value="globalDiv" />
				</ui:include>
				<ui:include src="/filter/filterSuggest.xhtml">
					<ui:param name="filterWidth" value="240" />
					<ui:param name="filter" value="#{ruleUnitTestLogBrowser.filter}" />
					<ui:param name="filterId" value="rule" />
					<ui:param name="filterName" value="Tested rule" />
					<ui:param name="filterForm" value="globalDiv" />
				</ui:include>
			</ui:define>
			<ui:define name="moreColumns">
				<rich:column>
					<f:facet name="header">Tested rule</f:facet>
					<g:link value="#{entry.unitTest.testedRule}" />
					<h:outputText value=" (#{entry.testedVersion})" />
				</rich:column>
			</ui:define>
		</s:decorate>
	</ui:define>
</ui:composition>

 

Create/Edit unit tests

Abstract class: net.ihe.gazelle.validator.ut.action.UnitTestEditorManager <T extends UnitTest>: implements the abstract methods and append the @Create annotation on the init() method. This init method shall be used to retrieve the unit test to edit (if no parameter is given in the URL, instanciate a new unit test). Use a PAGE scope. How files are appended to the test case is to be defined by you.

XHTML template: /unittesting/unitTestEditorTemplate.xhtml

The following parameters are expected:

  • managedBean : reference to the bean which extends UnitTestEditorManager, eg. #{ruleUnitTestEditorManager}
  • utManagerBean: reference to the bean which extends UnitTestManager, eg. #{ruleUnitTestManager}

In addition, you can add:

  • a panel containing information about the object of the unit test : infoOnValidatedObject
  • extract components to render the additional attributes of your unit tests: extraUnitTestAttributes
  • a panel to create a new input file: newInputCreation
  • the button to create a new file: buttonToCreateNewFile

Examples

This module is used in GazelleXValidatorRuleEditor (classes and xhtml files are respectively in gazelle-x-validation-ejb and gazelle-x-validation-war modules).

Extra tips

On the Unit test log browser page, you may want to add filters to search by tested entity. This entity is not an attribute of the UnitTest abstract class, as a consequence, you will have to implement the addPathToCriteria abstract method declared in UnitTestLogBrowser using the HQLSafePathEntityCriterion constructor. An example is given below. 

@Override
	protected void addPathToCriteria(HQLCriterionsForFilter<UnitTestLog> criteria) {
		criteria.addCriterion(new HQLSafePathEntityCriterion(Rule.class, "rule", "unitTest.testedRule"));
		criteria.addCriterion(new HQLSafePathEntityCriterion(GazelleCrossValidatorType.class, "validator", "unitTest.testedRule.gazelleCrossValidator"));
	}

This way, two new suggest filters are available with filterId equals to rule and validator.