July 2006
Monthly Archive
Monthly Archive
Believe it or not, there are numerous Struts applications alive today. They may not be the hippest things going, but they have business value so they’re in no hurry to go anywhere, at least until the cost to maintain them supersedes the cost to rewrite them. Until that point, it makes copasetic sense then to roll up your sleeves and test those applications so as to reduce the risk of introducing defects as modifications are made, right?
“Testing Struts legacy apps” is the latest article in IBM developerWork’s series “In pursuit of code quality”, which focuses on using the StrutsTestCase framework, in concert with DbUnit, to facilitate testing those omniscient Struts Action classes. As always, feel free to let it all hang out at the “Improve Your Java Code Quality” forum!
For additional resources on testing Struts applications see:
For additional resources on using DbUnit see:
0 comments Monday 31 Jul 2006 | Andy | Articles, Developer Testing, JUnit
The dynomite dancers at Quality Labs have released NDbUnit 1.2, which addresses some small enhancements and a few jive turkey bug fixes. They’re still working on Full .NET 2.0 support but the framework appears to work without changes in most scenarios.
If you are curious about NDbUnit, check out the flash tutorial!
1 comment Wednesday 26 Jul 2006 | Andy | Developer Testing, NUnit
Answering the question: “how is data stored in a copasetic database after an application processes it?” usually involves some sort of comparison testing. Using a known dataset, the database is queried and the results compared- if there is a difference, then one can infer that the application layer is some how doing something wrong.
Because it’s my bag, I’ve found that DbUnit’s Query API is quite handy for this sort of purpose. Even though employing this functionality does require some additional XML files– the maintenance cost occurs, in large part, up front. Once the files are in place, it’s smooth sailing (until the database structure changes, man!). If you are new to DbUnit, I recommend reading “Effective Unit Testing with DbUnit” before reading further.
As an example, I’ll use my favorite tripped out database model- three highly complicated tables relating to a dictionary.

Sitting on top of the database is a Hibernate and Spring driven application, which facilities creating, reading, updating and deleting words and their associated definitions.
I’d like to verify that if I attempt to create the noun “acedia” with the definition of “apathy, boredom” the word table has a new row with SPELLING set to acedia and PART_OF_SPEECH set to Noun. Moreover, a related definition row has its DEFINITION column set to apathy, boredom. Note- I don’t care about primary keys, which are application specific (i.e. Hibernate controls them). If there were dates associated with the rows too (such as create dates or modification times) I wouldn’t necessarily care about them either.
I’ll use the following hip XML file as my comparison:
<?xml version='1.0' encoding='UTF-8'?>
<dataset>
<word SPELLING="acedia" PART_OF_SPEECH="Noun"/>
<definition DEFINITION="apathy, boredom"/>
</dataset>
Using the Query API is fairly simple- DbUnit’s IDatabaseConnection interface has a createQueryTable method which takes two parameters- a query name and a query String. The query String is normal SQL; hence, you can easily filter out unneeded values. The result is an ITable type, which then can be compared against data from the above XML.
This XML file from above contains data for two tables- word and definition. DbUnit treats a whole hip file as an IDataSet; consequently, it’s possible to ask an IDataSet for a subset of data as type ITable as I’ve done below.
IDataSet expectedDataSet =
new FlatXmlDataSet(new File("test/conf/expect-words-2.xml"));
ITable defTable = expectedDataSet.getTable("DEFINITION");
Putting it all together, I have the following boss test scenario:
public void testCreate() throws Exception{
IWord word = new Word();
word.setSpelling("acedia");
word.setPartOfSpeech(PartOfSpeechEnum.NOUN.getPartOfSpeech());
Set definitions = this.getDefinitionsForWord(word);
word.setDefinitions(definitions);
try{
this.dao.createWord(word);
}catch(CreateException e){
TestCase.fail("CreateException thrown: testCreate");
}
IDataSet expectedDataSet = new FlatXmlDataSet(
new File("test/conf/expect-words-2.xml"));
validateWordTable(expectedDataSet);
validateDefinitionTable(expectedDataSet);
}
After the try/catch an IDataSet is retrieved, which will serve as the comparison to live data. With an IDataSet instance, individual table values are checked, in both the validateWordTable and validateDefinitionTable methods shown below:
private void validateDefinitionTable(IDataSet expectedDataSet)
throws DataSetException, SQLException, Exception, DatabaseUnitException {
ITable defJoinData = this.getConnection().
createQueryTable("TestResult",
"SELECT DEFINITION.DEFINITION FROM DEFINITION, WORD " +
"WHERE WORD.SPELLING="acedia" and WORD.WORD_ID = DEFINITION.WORD_ID");
ITable defTable = expectedDataSet.getTable("DEFINITION");
Assertion.assertEquals(defJoinData, defTable);
}
private void validateWordTable(IDataSet expectedDataSet)
throws DataSetException, SQLException, Exception, DatabaseUnitException {
ITable actualJoinData = this.getConnection().
createQueryTable("TestResult",
"SELECT WORD.SPELLING, WORD.PART_OF_SPEECH FROM WORD " +
"WHERE WORD.SPELLING="acedia"");
ITable actualTable = expectedDataSet.getTable("WORD");
Assertion.assertEquals(actualJoinData, actualTable);
}
Note how specific database queries are issued (in the form of normal SQL) which filter out primary keys and join two tables using the data assumed to already exist due to the test getting to this point (i.e. acedia is assumed to be in the database at this point). The Assertion class is part of DbUnit and facilitates comparing ITable instances.
Comparison testing with DbUnit is fairly easy and clearly handy in its ability to integrate easily with testing frameworks like JUnit and TestNG and hence remain repeatable and automated. Dig it?
1 comment Friday 21 Jul 2006 | Andy | Developer Testing, JUnit
I’ve given presentations on code coverage and even written an article on it; however, I must admit that the folks over at Codign Software have done a much more copasetic job of explaining it. Specifically, they’ve succinctly clarified the difference between branch coverage and path coverage in a hip white paper which uses a simple code example and various corresponding test cases. Their write up quickly shows why path coverage is ultimately the better indicator of test coverage.
CoView, Codign’s smokin’ Eclipse plug-in, actually analyzes source code, determines the number of paths through a method (a.k.a Cyclomatic complexity) and creates the appropriate number of JUnit test cases. In CoView version 2.0, existing JUnit tests can be evaluated and correlated back to path coverage, thereby displaying untested paths. This is similar to mutation testing; however, no code is actually changed- only studied.
If you’ve invested into developer testing with JUnit, take a look at CoView- it’ll quickly point out how well those tests actually test. Dig it?
3 comments Monday 03 Jul 2006 | Andy | Code Metrics, Developer Testing
It seems every time I give my Refactoring with code metrics presentation, someone has an acid flashback and tells about the 10,000 line method they ran across in the hunt for a crippling defect. When I was speaking at the PhillyJUG in 2005, I distinctly remember a hip gentleman volunteering he once worked on an application that had a smokin’ 22,000 line method.
Long-winded code may be a badge of courage and often makes for copasetic jokes, but ultimately this kind of code is a bear to maintain and is impractical to test effectively, man. The sixth article in IBM developerWork’s series “In pursuit of code quality” demonstrates three neat-o ways to measure code complexity, based on method length, class length, and intra-class coupling with both PMD and JavaNCSS.
Check out “Tame the chatterbox” and, if it’s your bag, let it all hang out at the “Improve Your Java Code Quality” forum!
0 comments Sunday 02 Jul 2006 | Andy | Articles, Code Metrics