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.
https://scm.gforge.inria.fr/svn/gazelle/Maven/gazelle-validator-unit-testing/trunk
http://gazelle.ihe.net/jenkins/job/gazelle-validator-unit-testing/
http://gazelle.ihe.net/jira/browse/VUT
<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>
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
The result of each run is stored in database using the UnitTestLog entity which contains the following attributes (extract):
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.
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.
Currently, three pages are pre-defined:
Abstract classes for the backend beans and XHTML templates have been defined and are available in the module.
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(); } //... }
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:
In addition, you can add:
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:
In addition, you can add:
<!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>
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:
In addition, you can add:
This module is used in GazelleXValidatorRuleEditor (classes and xhtml files are respectively in gazelle-x-validation-ejb and gazelle-x-validation-war modules).
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.