Articles
Enterprise Architecture
Test Categorization Techniques with TestNG
Pages:
1,
2,
3
Component test have a few aliases, such as subsystem tests or integration tests. Regardless of the desired term, these tests verify portions of an application and may even require a fully installed system or a more limited set of external dependencies, such as databases, file systems, or network endpoints. These tests, in essence, verify that different components interact to produce the expected combined behavior.
A typical component test requires a seeded database; moreover, the test itself may verify behavior across architectural boundaries. Because large amounts of code are exercised by component tests, more code coverage is achieved; however, these tests have the tendency to take longer to run than unit tests.
Because component tests have a cost associated with them—dependencies have to be put in place and configured—they should not be run every time a build is executed. Instead, they should be run at regular intervals. Remember, while these tests alone may only take a few seconds, as more component tests are added to a suite the total test time increases, often quite quickly.
For example, the component test below utilizes DbUnit to seed an underlying database. The set-up alone in this test case takes longer than most unit tests should take to run.
//imports removed...
public class WordDAOImplTest {
private WordDAO dao = null;
/**
* @testng.before-method = "true"
*/
private void setUp() throws Exception {
final ApplicationContext context =
new ClassPathXmlApplicationContext(
"spring-config.xml");
this.dao = (WordDAO) context.getBean("wordDAO");
}
/**
* @testng.before-class = "true"
*/
public void oneTimeSetUp() throws Exception{
final IDatabaseConnection conn =
this.getConnection();
final IDataSet data = this.getDataSet();
try{
DatabaseOperation.CLEAN_INSERT.execute(conn, data);
}finally{
conn.close();
}
}
/**
* @testng.test
*/
public void createWord() {
final IWord word = new Word();
word.setSpelling("pabulum");
word.setPartOfSpeech(
PartOfSpeechEnum.NOUN.getPartOfSpeech());
final IDefinition defOne = new Definition();
defOne.setDefinition("food");
defOne.setWord(word);
final Set defs = new HashSet();
defs.add(defOne);
word.setDefinitions(defs);
try{
this.dao.createWord(word);
}catch(CreateException e){
Assert.fail("CreateException was thrown");
}
}
private IDataSet getDataSet()
throws IOException, DataSetException {
return new FlatXmlDataSet(
new File("test/conf/words-seed.xml"));
}
private IDatabaseConnection getConnection()
throws ClassNotFoundException, SQLException {
final Class driverClass =
Class.forName("org.gjt.mm.mysql.Driver");
final Connection jdbcConnection = DriverManager.
getConnection("jdbc:mysql://localhost/words",
"words", "words");
return new DatabaseConnection(jdbcConnection);
}
}
Component tests don't always depend on databases, however. For example, relying on file systems creates a coupling that increases configuration complexity and, in some cases, time. For instance, the component test below uses XMLUnit to verify generated XML. Note that the test relies on a file system path to compare two XML documents.
//imports removed...
public class BatchDepXMLReportValidationTest {
/**
* @testng.before-class = "true"
*/
protected void configure() throws Exception {
XMLUnit.setControlParser(
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setTestParser(
"org.apache.xerces.jaxp.DocumentBuilderFactoryImpl");
XMLUnit.setSAXParserFactory(
"org.apache.xerces.jaxp.SAXParserFactoryImpl");
XMLUnit.setIgnoreWhitespace(true);
}
private Filter[] getFilters(){
return new Filter[] {
new RegexPackageFilter("java|org"),
new SimplePackageFilter("junit.")
};
}
private Dependency[] getDependencies(){
return new Dependency[] {
new Dependency("com.vanward.resource.XMLizable"),
new Dependency("com.vanward.xml.Element")
};
}
/**
* @testng.test
*/
public void assertToXML() throws Exception{
BatchDependencyXMLReport report =
new BatchDependencyXMLReport(
new Date(9000000), this.getFilters());
report.addTargetAndDependencies(
"com.vanward.test.MyTest", this.getDependencies());
report.addTargetAndDependencies(
"com.xom.xml.Test", this.getDependencies());
Diff diff = new Diff(new FileReader(
new File("./test/conf/report-control.xml")),
new StringReader(report.toXML()));
Assert.assertTrue(
diff.identical(),"XML was not identical");
}
}
While component tests shouldn't be executed every time a build is run, it's a good bet to run them before committing code into a repository. In a continuous integration environment it's probably a good idea to run these at frequent intervals, such as hourly.