Automating Fit Tests in Fitnesse Server from Cruise Control, Hudson, Bamboo via Ant

Here’s an example of calling Fit tests defined in Fitnesse from Ant. This can be called from a continuous integration server like cruise control, hudson, bamboo, luntbuild, pmeasy:

<target name="fitnesse-tests"
           description="fitnesse-tests target can be called from CC"
           depends="custom-init, compile-fit"
>
 
 <java classname="fitnesse.runner.TestRunner"
           fork="true" resultproperty="FitRegressionResult">
    <arg line="-results fit-tests/reports/fitnesse-results.html
          10.99.99.99 80 FitnessePageName"/>
    <classpath><path refid="custom.classpath" /></classpath>
 </java>
<!-- xml file processed by cruise control -->
 <java classname="fitnesse.runner.FormattingOption" fork="true">
  <arg line="fit-tests/reports/fitnesse-results.html xml
             ${fit.report.dir}/fit-results.xml
             10.99.99.99 80 FitnessePageName"/>
  <classpath><path refid="custom.classpath" /></classpath>
 </java>
 
 <!-- fail if any fit test errors -->
 <condition property="fit-failures">
   <equals arg1="${FitRegressionResult}" arg2="0"/>
 </condition>
 <fail message="${FitRegressionResult} Fitnesse test(s) failed."
        unless="fit-failures" />
</target>

Fitnesse is an excellent communication and collaboration tool between developers, QA and clients. It works well in agile development environments where emphasis and priority is put on automating tests.

If you have any questions, feel free to contact Todd McGrath at http://www.supergloo.com/contact.htm

Unit Testing with StrutsTestCase for JUnit Tutorial

As the name implies, StrutsTestCase is a tool for testing your Struts code. In this article, I’ll cover:

  • What is StutsTestCase?
  • How?
  • When?
  • Where?

This article assumes the reader has familiarity and experience with Junit.

“StrutsTestCase for JUnit is an extension of the standard JUnit TestCase class that provides facilities for testing code based on the Struts framework. StrutsTestCase provides both a Mock Object approach and a Cactus approach to actually run the Struts ActionServlet, allowing you to test your Struts code with or without a running servlet engine.” -

In this article, we’ll discuss and examples using the MockObject approach; not Cactus.

StrutsTestCase reads the struts config file and loads up validators, plug-ins, forwards, etc. In other words, it can mock what happens when a servlet container is started without actually requiring a servlet container.

Just like JUnit – Extend a Class and follow a method naming convention

You have the same access to setUp() and tearDown():

public class TestLoginAction extends MockStrutsTestCase {
    public void setUp() { super.setUp(); }
    public void tearDown() { super.tearDown(); }
}

Extends JUnit’s TestCase, so access to all the assert*. Differentiates itself by using verify* methods.

Form validation example:

public void testNonNumberEntry() {
 
	addRequestParameter("txtFlatPercent", "arg1"); //add bogus percent
    	setRequestPathInfo("/calculate");
 
    	CalculatorValidatorActionForm form = new CalculatorValidatorActionForm();
    	form.setTxtName("Account Name");
    	form.setChkOptionRate("Flat");
    	form.setTxtAccountVal("55000");
    	form.setTxtFixedPer("string");
    	setActionForm(form);
 
    	actionPerform();
 
    	verifyActionErrors(new String[] { "prompt.txtFlatPercent"});
 
	}
 
 
    public void testFlatPercentValidationWithOutExceptionRequest() {
 
    	setRequestPathInfo("/calculate");
 
    	CalculatorValidatorActionForm form = new CalculatorValidatorActionForm();
    	form.setTxtName("Account Name");
    	form.setChkOptionRate("Flat");
    	form.setTxtFlatPercent("49");
    	form.setTxtAccountVal("500000");
    	setActionForm(form);
 
    	actionPerform();
 
    	verifyForward("flat");
    	assertNull(getMockRequest().getSession().getAttribute(ConstantsInterface.CONFIRM_EXCEPTION_REQUIRED));
 
    }

Struts Plug-In Validation

try {
	FeeCalcServiceFactory serviceFactory = (FeeCalcServiceFactory)getActionServlet().getServletContext().getAttribute(ConstantsInterface.FEECALC_SERVICE_FACTORY_KEY);
	assertNotNull(serviceFactory.createService());
  } catch (Exception e) {
	log.error(e);
	fail("testFactory - should not have thrown exception when obtaining service");
  }
}
 
public void testGetRates() {
   try {
	FeeCalcServiceFactory serviceFactory = (FeeCalcServiceFactory)getActionServlet().getServletContext().getAttribute(ConstantsInterface.FEECALC_SERVICE_FACTORY_KEY);
	FeeCalcService service = serviceFactory.createService();
	// should never be empty
	assertTrue(service.getRates().size() >= 0);
 
   } catch (Exception e) {
	log.error(e);
	fail("testGetRates - should not have thrown exception");
   }
}

StrutsTestCase Compatibility:

  • Struts 1.2, 1.1
  • Tiles
  • Sub-applications
  • Java Servlet 2.2, 2.3, and 2.4 specifications
  • JUnit 4.0

StrutsTestCase Gotchas:

  • WEB-INF - “By default, the Struts ActionServlet will look for the file WEB-INF/struts-config.xml, so you must place the directory that contains WEB-INF in your CLASSPATH. If you would like to use an alternate configuration file, please see the setConfigFile() method for details on how this file is located.”
  • Does not support Struts 1.3 or 2

Example of Java Acceptance Testing with Fit

Fit testing provides a way for non-developers to participate in test driven development. Fit testing facilitates collaboration and communication in applications that need to verify results from calculations. From the Fit testing website, http://fit.c2.com/:

Great software requires collaboration and communication. Fit is a tool for enhancing collaboration in software development. It’s an invaluable way to collaborate on complicated problems–and get them right–early in development.

Let’s run an example of using Fit tests in your Java application. This example assumes you are using Ant to build.

First, pick up the AntFit ant task from http://www.cmdev.com/antfit/. This will provide the code to call Fit tests from your Ant build file.

<taskdef name="antfit" classname="com.cmdev.fit.ant.FitTask" classpathref="antfit.classpath">
</taskdef>

and calling it:

<target name="test-fit" depends="init,compile" description="Executes the Fit tests">
<antcall target="populateDb"></antcall></target>
 
<!-- This uses a whole different set of folders -->
 
<property name="our.fit.dir" value="${basedir}/../fit"></property>
<property name="our.fit.src.dir" value="${our.fit.dir}/src"></property>
<property name="our.fit.test.dir" value="${our.fit.dir}/tests"></property>
<property name="our.fit.report.dir" value="${our.report.dir}/fit"></property>   <mkdir dir="${our.fit.report.dir}"></mkdir>  <!-- Now compile the fit tests -->
<property name="our.fit.compiled.dir" value="${our.work.dir}/compiled-fit"></property>   <mkdir dir="${our.fit.compiled.dir}">
<javac srcdir="${our.fit.src.dir}" destdir="${our.fit.compiled.dir}" target="1.4" fork="true" memoryMaximumSize="512m">
<classpath></classpath></javac></mkdir>
<path refid="our.classpath.buildtime"></path>
<path location="${our.test.compiled.dir}"></path> <!-- Now execute the fit tests -->
<antfit destdir="${our.fit.report.dir}" usewiki="false" fork="true">
<classpath></classpath></antfit>
<path location="${our.fit.compiled.dir}"></path>
<path refid="our.classpath.buildtime"></path>
<path location="${our.test.compiled.dir}"></path> <fileset dir="${our.fit.test.dir}">
<include name="**/*.html"></include>
</fileset>

In this example, we are not going to use a wiki (useWiki=”false”) for creating tests. We can cover that in a later example.Next, let’s look at some Java source code for Fit tests:

import java.text.NumberFormat;
import org.apache.commons.lang.StringUtils;
import util.RateFetcher;
import com.feecalc.vo.choice.BlendedResult;
import fit.ColumnFixture; 
/**
* Automates testing the standard and discount fee for the standard tiers. Does all of its
* calculations based on Equity, assuming that if the code works for Equity, it will work for any of
* them.
*
* @author robert.c.fischer
*/
public class FeeFixture extends ColumnFixture {

public double customRate;
protected static final String ZERO = “0″;
public double value;
public double discount;
public String assetType;

/**
* @return The standard fee.
*/
public String standardFee() {
return moneyToString(generateBlendedResult().getStandardFeeAnnual());
}

/**
* @return The discount fee.
*/
public String discountFee() {
return moneyToString(generateBlendedResult().getDiscountedFeeAnnual());
}

/**
* @return The discount blended rate
*/
public String discountRate() {
return percentToString(generateBlendedResult().getDiscountBlendedRate());
}

/**
* @return The standard blended rate
*/
public String standardRate() {
return percentToString(generateBlendedResult().getStandardBlendedRate());
}

/**
* @return The standard internal fee (post-”haircut” amount)
*/
public String internalStandardFee() {
return moneyToString(generateBlendedResult().getStandardRepFeeAnnual());
}

/**
* @return The discounted internal fee (post-”haircut” amount)
*/
public String internalDiscountFee() {
return moneyToString(generateBlendedResult().getDiscountedRepFeeAnnual());
}

/**
* @return The standard fee.
*/
public String getProposedClientDiscount() {
return percentToString(generateBlendedResult().getProposedClientDiscount());
}

protected static final String moneyToString(final double value) {
final NumberFormat format = NumberFormat.getInstance();
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
format.setGroupingUsed(false);
return format.format(value);
}

protected static final String percentToString(final double value) {
final NumberFormat format = NumberFormat.getInstance();
format.setMinimumFractionDigits(2);
format.setMaximumFractionDigits(2);
format.setGroupingUsed(false);
return format.format(value);
}

protected BlendedResult generateBlendedResult() {

final BlendedResult result = new BlendedResult();

// Set the rates
setEquityRates(result);
setCashRates(result);
setMutualRates(result);

// Set the test values provided by the user
if (StringUtils.isBlank(assetType) || assetType.toLowerCase().startsWith(”eq”)) {
result.setEquityDiscount(discount);
result.setEquityAmount(value);
result.setEquityRateTier(5, customRate);
} else {
result.setEquityDiscount(0);
result.setEquityAmount(0);
}
if (StringUtils.isNotBlank(assetType) && assetType.toLowerCase().startsWith(”mu”)) {
result.setMutualAmount(value);
result.setMutualDiscount(discount);
result.setMutualRateTier(5, customRate);
} else {
result.setMutualAmount(0);
result.setMutualDiscount(0);
}
if (StringUtils.isNotBlank(assetType) && assetType.toLowerCase().equals(”ca”)) {
result.setCashAmount(value);
result.setCashDiscount(discount);
result.setCashRateTier(5, customRate);
} else {
result.setCashAmount(0);
result.setCashDiscount(0);
}

return result;
}

private static final void setEquityRates(final BlendedResult result) {
final double[] equityRates = new RateFetcher().getEquityRates();
if (equityRates.length < 4) {
throw new RuntimeException(”Only have ” + equityRates.length
+ ” equity rates — need 4″);
}
for (int i = 0; i < equityRates.length; i++) {
result.setEquityRateTier(i + 1, equityRates[i]);
}
}

private static final void setCashRates(final BlendedResult result) {
final double[] cashRates = new RateFetcher().getCashRates();
if (cashRates.length < 4) {
throw new RuntimeException(”Only have ” + cashRates.length + ” cash rates — need 4″);
}
for (int i = 0; i < cashRates.length; i++) {
result.setCashRateTier(i + 1, cashRates[i]);
}
}

private static final void setMutualRates(final BlendedResult result) {
final double[] mutualRates = new RateFetcher().getMutualRates();
if (mutualRates.length < 4) {
throw new RuntimeException(”Only have ” + mutualRates.length
+ ” mutual rates — need 4″);
}
for (int i = 0; i < mutualRates.length; i++) {
result.setMutualRateTier(i + 1, mutualRates[i]);
}
}
}
Here’s the source that’s used in the Fit tests:

feecalc.FlatRateFeeFixture  
cash equity mutual flatRate assetSum() flatRateFee() comparisonFee()
300000 100000 100000 1 500000 5000.0 4975.0
4000000 4000000 4000000 0.25 12000000 30000.00 93875.00
100000 10000 10000 0.50 120000 600.00 850.00
5000 5000 5000 0.75 15000 112.50 200.00
130000 100000 10000 1.00 240000 2400.00 2800.00

And here is an example of the results after the Java Fit tests are run:

feecalc.FlatRateFeeFixture  
cash equity mutual flatRate assetSum() flatRateFee() comparisonFee()
300000 100000 100000 1 500000 5000.0 4975.0
4000000 4000000 4000000 0.25 12000000 30000.00 93875.00
100000 10000 10000 0.50 120000 600.00 850.00
5000 5000 5000 0.75 15000 112.50 200.00
130000 100000 10000 1.00 240000 2400.00 2800.00

Yae, all green. If there was an incorrect result, the errant result table cell would be red instead of green.

Hat tip to my co-worker Robert Fischer for writing the Java code and Fit test HTML.

More information on Fit testing can be found at:
http://fit.c2.com/

And for those that want a physical book:

DLL from Java with Jawin

Do you need to use a Windows DLL from Java? I recommend investigating the open source tool: Jawin. I found used this tool in order to call a DLL that converts HTML to RTF. After struggling with various JNI tutorials, Jawin was refreshing. What makes this tool outstanding is the jawinBrowser. Here’s a tutorial on calling a DLL from Java:

1. Download Jawin
2. In a command prompt, go to the typebrowser/ directory
3. java -jar jawinBrowser.jar

This will start the jawinBrowser that allows you to generate Java code to interact with the Windows DLL.

4. Start a new project and name it anything you wish
5. Next step is to point to the DLL - click the “New” button and select the DLL
6. Highlight the DLL and then double click the configuration options on the left hand side:

Calling DLL from Java with Jawin

7. Change the java package name to something appropriate. In my example, I converted it to com.supergloo.htmlconvert.
8. Specify an output directory; e.lg /tmp or c:/temp

9. Click Ok.

10. From the menu bar, select Code Generation->Generate Full Code
11. Next, from the menu bar, select Code Generation-> Save Java Files

You now have java src files to call your DLL methods! (check the directory you specified in step 8).

Going further, you will have an IConverter class that you can call. In my case, I used the following:

public static String convert(String htmlString) throws COMException{
String rtfString = "";
//Initialize service COM objects
Ole32.CoInitialize();
IConverter iconv = new IConverter("Html2Rtf.Converter");

//Set properties of coversions. See help for Sautin
iconv.setPreserveTables(true);
iconv.setPreserveNestedTables(true);
iconv.setPreserveTableWidth(true);
iconv.setPreserveImages(true);
iconv.setPreserveFontFace(true);
iconv.setPreserveFontColor(true);
iconv.setRtfLanguage(eLanguage.l_English);
//rtfString = iconv.Convert(htmlString, “”, “”);
rtfString = iconv.ConvertFileToString(htmlString, “”, “”);

//UnInitialize service COM objects
Ole32.CoUninitialize();
return rtfString;
}

To use in your Java project, make sure to include jawin.jar and jawin-Stubs.jar in your classpath.

You can drop the Jawin.dll in to %WINDOWS%\system32 or specifically set the PATH and library path when invoking your Java app; e.g. here’s a .bat file example:

set PATH=%PATH%;.\dll
set CLASSPATH=.\bin;.\lib\jawin.jar;.\lib\jawin-stubs.jar
java -Djava.library.path=.\dll Test

ServiceCycle is a registered trademark of Supergloo, inc..