
TestUtil: By Example
Introduction to TestUtil
TestUtil is a Java open source low-effort
automated method testing strategy and tool for verifying all matching accessor
methods on mutable objects with a single line of unit test code. It is
compatible with both in-container and outside-the-container strategies
extending JUnit.
A Testing Dilemma
A decision confronting an application
development team committed to testing – is whether to verify accessor methods
(“getters” and “setters”) in a unit test context. On one hand, invoking methods with presumably
little logic seems pedantic. On the
other hand, a simple copy and substitute error can result in a difficult to
detect (persisted) data integrity problem.
The following sample Business Object (BO) class contains a coding
error:
public class TestUtilSampleBO extends BaseBO {
private
String attributeOne;
private
String attributeTwo;
/**
* @return Returns the attributeOne.
*/
public
String getAttributeOne() {
return
this.attributeOne;
}
/**
* @param attributeOne The attributeOne to set.
*/
public
void setAttributeOne(String attributeOne) {
this.attributeOne
= attributeOne;
}
/**
* @return Returns the attributeTwo.
*/
public
String getAttributeTwo() {
return
this.attributeOne; // ß Copy & substitute mistake.
}
/**
* @param attributeTwo The attributeTwo to set.
*/
public
void setAttributeTwo(String attributeTwo) {
this.attributeTwo
= attributeTwo;
}
TestUtil was originally intended to catch
this type of coding error.
Examples
TestUtil was recently utilized during a
“Fortune 5” (company) application development project – let’s call it Fortune5. Approximately one-third of the developers
(including Architects) and the Business Analysts were co-located in North
America, and approximately two-thirds of the developers were in
Individual developers were expected to
include single object accessor method verification for all Business Objects
(BOs) and Struts Form Beans within their automated method (or JUnit) tests – as
follows:
/**
* Verify accessor methods of a mutable
business object.
*/
public void testPersonBO() {
assertTrue(TestUtil.verifyMutable(new PersonBO(), 1, 0));
}
Invoking the verifyMutable() method provided
developers with timely feedback for eliminating simple coding errors on a
single class. In addition, the
Consulting Architect began reviewing the entire code base with the verifyDirectory() method after
TestUtil v. 1.1 was released in November, 2005:
/**
* Verify all accessor methods in project source.
*/
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false);
}
The following Verification Chronicle
(including lessons learned) focuses on the TestUtil verifyDirectory() method. In addition, recommended practices for the
TestUtil verifyMutable()
method
are inferred. In May of 2006, the
Chronicle was updated to demonstrate Javadoc annotations introduced with
TestUtil v. 2.0.
Note: You will see the numbers recorded as
“Verify Directory Summary Results” fluctuate since the Chronicle covers
multiple weeks while development was in progress.
The Verification Chronicle
Run
#1 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 103 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1605 Successful
Verification(s); 0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1556 Successful
Verification(s); 33 Warning(s); 16 Glitch(es); 3 Caution(s)
****************************************************************************************************
Run
#1 Log Observation(s):
Fortune5 exceptions extend
from a corporate J2EE Platform Abstraction;
The exceptions are self-logging (and self-emailing when declaratively
configured). Therefore, a side-effect of
TestUtil instantiating Fortune5
application exceptions was numerous stack traces cluttering the log - when
exceptions don’t have matching accessor methods requiring verification anyway!
Run
#1 Response:
The following representative entries were
made to the testutil-config.xml descriptor
document causing Fortune5 exception
classes to be excluded from TestUtil verification.
<Element
aClassName="com.obfuscated.exceptions.Fortune5CheckedException"
cautionOrSkip="skip">true</Element>
<Element
aClassName="com.obfuscated.exceptions.Fortune5RuntimeException"
cautionOrSkip="
skip ">true</Element>
<Element
aClassName="com.obfuscated.exceptions.DuplicateKeyException"
cautionOrSkip="
skip ">true</Element>
<Element
aClassName="com.obfuscated.exceptions.ExternalProblemException"
cautionOrSkip="
skip ">true</Element>
<Element
aClassName="com.obfuscated.exceptions.NoInstancesFoundException"
cautionOrSkip="
skip ">true</Element>
Note: If a specific Fortune5 exception contained a matching accessor pair, a
custom-coded unit test could be created.
Note: With the introduction of TestUtil v.
2.0 a Javadoc tag could be used instead of editing the optional testutil-config.xml descriptor document.
/**
* @author MDT (MarvinToll@gtcGroup.com)
* @since v. 2.0
* @excludeClass skip
*/
public class
ExcludeClassUsingJavadocTagBO extends BaseTestBO {
// Methods here.
}
Run
#2 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 108 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1605 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1556 Successful
Verification(s); 33 Warning(s); 16 Glitch(es); 3 Caution(s)
****************************************************************************************************
Run
#2 Log Observation(s):
Fortune5 JSP tags extend javax.servlet.jsp.tagext.TagSupport. It turns out TagSupport has setValue() and getValue() methods which are
not TestUtil verifiable.
Run
#2 Response:
At this point a choice exists; should the
entire class be excluded or just the offending method? A quick perusal of application custom tags
indicate a general absence of getter/setter combinations. So, perhaps it makes sense to exclude the
classes entirely. Now a second choice
exists; should the exclusion be recorded as the default “caution” or a “skip”
on the “Verify Directory Summary Results”?
The default is chosen since it provides greater ongoing visibility for
the exclusion decision – should the decision need to be revisited.
The following representative entries were
made to the testutil-config.xml descriptor
document causing Fortune5 JSP tag
classes to be ignored for TestUtil verification.
<Element
aClassName="com.obfuscated.tags.Fortune5NotAuthorizedTag"
cautionOrSkip="caution">true</Element>
<Element
aClassName="com.obfuscated.tags.Fortune5IsAuthorizedTag"
cautionOrSkip="caution">true</Element>
<Element
aClassName="com.obfuscated.tags.Fortune5GetValueTag"
cautionOrSkip="caution">true</Element>
Note: A side-effect of this exercise was
the removal of an obsolete tag from the code base.
Note: If a specific Fortune5 tag contained a matching accessor pair, a custom-coded
unit test could be created.
Note: With the introduction of TestUtil v.
2.0 a (simple) Javadoc tag could be used instead of editing the optional testutil-config.xml descriptor document.
/**
* @author MDT (MarvinToll@gtcGroup.com)
* @since v. 2.0
* @excludeClassFromTestUtilVerification
*/
public class ExcludeClassUsingJavadocTagBO
extends BaseTestBO {
// Methods here.
}
Run
#3 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1584 Successful
Verification(s); 0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1541 Successful
Verification(s); 30 Warning(s); 13 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#3 Log Observation(s):
Fortune5 has two (2)
occurrences of transforming attributes from upper to lower case within accessor
methods. This is considered a correct
implementation by the development team.
Glitch [#
1]: Setter = [setGlobalIdText()]; Getter = [getGlobalIdText()]
Randomly
set [S163540668] value does not match returned [s163540668] value.
Run
#3 Response:
The following representative entries were
made to the testutil-config.xml descriptor
document causing Fortune5 method
pairs to be excluded from verification.
<Configuration
name="exclude.method.pair.by.setter">
<Element
aMutableClassName="com.obfuscated.formbean.FunctionalForm"
theSetterMethodName="setGlobalIdText">true</Element>
<Element
aMutableClassName="com.obfuscated.formbean.PatternForm"
theSetterMethodName="setGlobalIdText">true</Element>
</Configuration>
Since exclusion of methods result in a
TestUtil “warning” (instead of a “glitch”), the unit test method was modified
to assert the two warnings.
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false, "", true, 2);
}
Note: Verification of excluded method
pairs, if desired, requires creating custom-coded unit tests.
Note: With the introduction of TestUtil v.
2.0 a “setter” Javadoc tag may indicate exclusion instead of the optional testutil-config.xml descriptor document.
/**
* @param globalIdText
* @excludeSetter
*/
public
void setGlobalIdText(String globalIdText) {
this.globalIdText
= globalIdText.toLowerCase();
}
Run
#4 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1582 Successful Verification(s);
2 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1541 Successful
Verification(s); 32 Warning(s); 11 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#4 Log Observation(s):
Fortune5 has seven (7)
occurrences of transformation logic associated with date processing. This is considered a correct implementation
by the development team.
Glitch [#
1]: Setter =
[setStringReleaseDate()]; Getter = [getStringReleaseDate()]
The
method [setStringReleaseDate] could not be invoked
[java.lang.reflect.InvocationTargetException] with [java.lang.String] parameter
type.
Run
#4 Response:
The following representative entry was made
to the testutil-config.xml descriptor
document causing Fortune5 method
pairs to be excluded from verification.
<Configuration
name="exclude.method.pair.by.setter">
<Element
aMutableClassName="com.obfuscated.bo.LeadershipBO"
theSetterMethodName="setStringReleaseDate">true</Element>
etc.
etc.
</Configuration>
Since exclusion of methods result in a
TestUtil “warning” (instead of a “glitch”), the unit test method was modified
to assert the additional seven (7) warnings – for a total of nine (9) warnings.
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false, "", true, 9);
}
Note: Discovery of these seven (7)
identical occurrences yielded a refactoring opportunity for removal of
duplicate code.
Note: With the introduction of TestUtil v.
2.0 a setter Javadoc tag may indicate exclusion instead of the optional testutil-config.xml descriptor document.
/**
* @param stringReleaseDate
* @excludeSetterFromTestUtilVerification
*/
public void setStringReleaseDate(String
stringReleaseDate) {
// String release date is manipulated.
stringReleaseDate = AppDateUtil.manipulateDate(stringReleaseDate);
}
Run
#5 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1575 Successful
Verification(s); 9 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1541 Successful
Verification(s); 39 Warning(s); 4 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#5 Log Observation(s):
Fortune5 has four (4)
occurrences of miscellaneous transformation logic applied in “getters”. This is considered a correct implementation
by the development team.
Glitch [#
1]: Setter =
[setPositionTaskBOList()]; Getter = [getPositionTaskBOList()]
The
method [getPositionTaskBOList] could not be invoked
[java.lang.reflect.InvocationTargetException] with [n/a] parameter type.
Glitch [#
1]: Setter =
[setStringDisplayOrder()]; Getter =
[getStringDisplayOrder()]
The
method [setStringDisplayOrder] could not be invoked
[java.lang.reflect.InvocationTargetException] with [java.lang.String] parameter
type.
Glitch [#
1]: Setter = [setStageBOList()]; Getter = [getStageBOList()]
The
method [getStageBOList] could not be invoked [java.lang.reflect.InvocationTargetException]
with [n/a] parameter type.
Glitch [#
1]: Setter =
[setActiveEmployeeBOList()]; Getter =
[getActiveEmployeeBOList()]
The
method [getActiveEmployeeBOList] could not be invoked
[java.lang.reflect.InvocationTargetException] with [n/a] parameter
Run
#5 Response:
The following four (4) entries were made to
the testutil-config.xml descriptor
document causing Fortune5 method
pairs to be excluded from verification.
<Configuration
name="exclude.method.pair.by.setter">
<Element
aMutableClassName="com.obfuscated.bo.PositionBO"
theSetterMethodName="setPositionTaskBOList">true</Element>
<Element
aMutableClassName="com.obfuscated.bo.CompetencyBO"
theSetterMethodName="setStringDisplayOrder">true</Element>
<Element
aMutableClassName="com.obfuscated.bo.ScaleBO"
theSetterMethodName="setStageBOList">true</Element>
<Element
aMutableClassName="com.obfuscated.bo.EmployeePositionBO"
theSetterMethodName="setActiveEmployeeBOList">true</Element>
</Configuration>
Since exclusion of methods results in a
TestUtil “warning” (instead of a “glitch”), the unit test method was modified
to assert the additional four (4) warnings – for a total of thirteen (13)
warnings.
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false, "", true, 13);
}
Note: The @excludeSetter Javadoc tag could be used instead
of editing the optional testutil-config.xml descriptor
document.
Run
#6 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1575 Successful
Verification(s); 13 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1545 Successful
Verification(s); 43 Warning(s); 0 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#6 Synchronization of Source Code:
The Fortune5
code base continues to mature – prompting a resynchronization with the source
code management system.
Run
#7 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1590 Successful
Verification(s); 13 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 1552 Successful
Verification(s); 45 Warning(s); 6 Glitch(es); 6 Caution(s)
****************************************************************************************************
Note: In case you were wondering about the
“Successful Verification(s)” calculation - the math works like this:
·
Anticipated:
1590 + 13 (Warnings) = 1603 total
matching pairs subject to verification
·
Results:
1552 + 45 (Warnings) + 6 (Glitches) = 1603
total matching pairs subject to verification
·
“Cautions”
and “Skips” refer to classes and are not included in method calculations
Run
#7 Log Observation(s):
After synchronization, three (3) classes
could not be instantiated with reflection for verification. Two of the classes require an HttpServletRequest constructor parameter and will necessitate
a custom-coded unit test.
* Caution
[# 1]: Instantiation [com.obfuscated.forms.DevFocusViewHelper] failure.
* Caution
[# 2]: Instantiation [com.obfuscated.util.AuthorizationPlugin] failure.
* Caution
[# 3]: Instantiation [com.obfuscated.base.BaseAuthorizationPlugin] failure.
Run
#7 Response:
The following two entries were made to the testutil-config.xml descriptor document causing Fortune5 authorization classes to be
ignored for TestUtil verification. It
turns out the DevFocusViewHelper class was
implemented inconsistently with the architectural patterns of the project and
requires refactoring - the appropriate developer was notified.
<Element
aClassName="com.obfuscated.util.AuthorizationPlugin"
cautionOrSkip="caution">true</Element>
<Element
aClassName="com.obfuscated.base.BaseAuthorizationPlugin"
cautionOrSkip="caution">true</Element>
The testAllAccessors() method remains
unchanged and one unanticipated “caution” will result. The unit test will not pass (run green) until
the DevFocusViewHelper class is refactored and the number
of anticipated “cautions” match the result.
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false, "", true, 13, 0, 5);
}
Note: The @excludeSetter Javadoc tag could be used instead
of editing the optional testutil-config.xml descriptor
document.
Run
#8 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1590 Successful
Verification(s); 13 Warning(s); 0 Glitch(es); 5 Caution(s)
* -----> Results: 1552 Successful
Verification(s); 45 Warning(s); 6 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#8 Log Observation(s):
After the last source code synchronization,
six (6) new “glitches” appeared.
Glitch [#
1]: Setter =
[setPositionCompetencyBOListForDisplay()];
Getter = [getPositionCompetencyBOListForDisplay()]
Glitch [#
2]: Setter =
[setPositionDetailBOListForDisplay()];
Getter = [getPositionDetailBOListForDisplay()]
Glitch [#
1]: Setter =
[setConcentrationHeadingList()]; Getter
= [getConcentrationHeadingList()]
Glitch [#
1]: Setter = [setEmployeeDevRO()]; Getter = [getEmployeeDevRO()] ß Three occurrences.
Run
#8 Response:
The three (3) occurrences relating to EmployeeDevRO were a side-effect of obsolete debug
statements. The applicable developer was
contacted and the debug statements subsequently removed. The following three (3) entries were made to
the testutil-config.xml descriptor
document causing Fortune5 method
pairs to be excluded from verification.
<Configuration
name="exclude.method.pair.by.setter">
<Element
aMutableClassName="com.obfuscated.bo.PositionBO"
theSetterMethodName="setPositionCompetencyBOListForDisplay">true</Element>
<Element
aMutableClassName="com.obfuscated.bo.PositionBO"
theSetterMethodName="setPositionDetailBOListForDisplay">true</Element>
<Element
aMutableClassName="com.obfuscated.presentation.forms.PositionListForm"
theSetterMethodName="setConcentrationHeadingList">true</Element>
</Configuration>
Since exclusion of methods result in a
TestUtil “warning” (instead of a “glitch”), the unit test method was modified
to assert the additional three (3) warnings – for a total of sixteen (16)
warnings.
public
void testAllAccessors() {
TestUtil.verifyDirectory("\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false, "", true, 16, 0,
5);
}
Note: The @excludeSetter Javadoc tag could be used instead
of editing the optional testutil-config.xml descriptor
document.
Run
#9 Summary Results:
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 106 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 1590 Successful
Verification(s); 16 Warning(s); 0 Glitch(es); 5 Caution(s)
* -----> Results: 1555 Successful
Verification(s); 48 Warning(s); 0 Glitch(es); 6 Caution(s)
****************************************************************************************************
Run
#9 Timeout For A Sanity Check:
Managing 48
“warnings” is growing cumbersome with one unit test. The number of “warnings” is an indicator of
the volume of transformation logic being coded within accessor methods. Some architects might discourage the practice
of using the verbs “set”, “get” and “is” for methods containing transformation
logic. Regardless of ones philosophical
stance on the issue – the immediate tactical reality dictates splitting the
single test into multiple categorized by high level package name. Let’s start with three unit tests.
Run
#10 Summary Results:
// Verification Complete (Green Bar)!
public
void testAllAccessors_Common() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.common");
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 21 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 21 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 21 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
public
void testAllAccessors_Administration() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.administration",
true,
0);
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 22 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 544 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 513 Successful Verification(s);
31 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
public
void testAllAccessors_Development() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.development",
true,
0);
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 47 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 974 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 959 Successful Verification(s);
15 Warning(s); 0 Glitch(es); 1 Caution(s)
****************************************************************************************************
Run
#10 Timeout For Sanity Check:
It is now evident
the majority of warnings (and transformation logic) are coming from the
“Administration” package. Further review
of the code base indicates there are four packages under “Administration” that
could be further decomposed. After
decomposition – the 46 “warnings” are attributable to three packages.
Run
#11 Summary Results For Three Remaining “Red Bar” Tests:
public
void testAllAccessors_Administration_Common() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.administration.common",
true,
0);
}
****************************************************************************************************
* TestUtil v. 2.0 - Verify Directory
Summary Results
* -------> Skips: 22 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 216 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 202 Successful Verification(s);
14 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
public
void testAllAccessors_Administration_Presentation() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.administration.presentation",
true,
0);
}
****************************************************************************************************
* TestUtil v. 2.0 - Verify Directory
Summary Results
* -------> Skips: 0 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 320 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 303 Successful Verification(s);
17 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
public
void testAllAccessors_Development() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.development",
true,
0);
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 47 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 974 Successful Verification(s);
0 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 959 Successful Verification(s);
15 Warning(s); 0 Glitch(es); 1 Caution(s)
****************************************************************************************************
Run
#11a (development package) Log Observation(s):
Of the fifteen (15) “warnings” in the
“development” package, two (2) were method pairs intentionally excluded from
verification in the testutil-config.xml descriptor
document and thirteen (13) were associated with list processing.
+
Verification Warning(s) - com.obfuscated.development.forms.DevelopmentForm +
Warning
[# 1]: Setter =
[setEmployeeSkillBO()]; Getter =
[getEmployeeSkillBO()]
Setter
first parameter [int] and getter return type
[com.obfuscated.development.bo.EmployeeSkillBO] are not TestUtil verifiable.
In the thirteen cases represented above, a
collection is “set” – however, the “getter” is used to return a specified
element of the collection. One could
argue a more standards-based approach would be to use a “getter” and “setter”
for the collection, and a method named similarly to getEmployeeSkillBoElement(int
index)
for returning an element of the collection.
Unfortunately, the discovery is a bit late in the development cycle for
the Consulting Architect to instigate a discussion of this coding practice.
Run
#11a (development package) Response:
Since all 15 TestUtil “warnings” were
accounted for, the unit test method was modified to assert fifteen (15)
warnings.
public
void testAllAccessors_Development() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.development",
true,
15);
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 47 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 959 Successful Verification(s);
15 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 959 Successful Verification(s);
15 Warning(s); 0 Glitch(es); 1 Caution(s)
****************************************************************************************************
Run
#11b (administration.presentation package) Log Observation(s):
Five (5) violations of the JavaBeans
specification within Struts form beans were detected. Since accessor methods are invoked
reflectively by the Jakarta Commons BeanUtil classes – there is no guarantee
which overloaded “setter” is invoked by a JRE.
+
Verification Warning(s) -
com.obfuscated.administration.presentation.forms.MessageForm +
Warning
[# 1]
Multiple
occurrences of setter [setSelectedBusinessGroup] precludes verification.
There are ten (10) occurrences of “setters”
whose method signature has no parameter.
+
Verification Warning(s) - com.obfuscated.administration.presentation.forms.ManageSkillListForm
+
Warning
[# 1]: Setter =
[setSelectedBusinessGroup()]; Getter =
[getSelectedBusinessGroup()]
The
setter signature has no parameter and can not be TestUtil verified.
Run
#11b (administration.presentation package) Response:
The five (5) JavaBeans specification
violations were remanded to the development team for resolution.
Investigation revealed the ten (10)
no-parameter “setters” are invoked in the context of instance initialization -
with considerable business logic contained therein. As a coding (and unit test) recommended
practice - one might prefer to have the business logic contained explicitly in
the constructor or an initialization method with “setters” serving a strictly
accessor role. Given the belated
detection within the project lifecycle and tight deadlines – the Consulting
Architect chose not to instigate a discussion of this coding practice.
Note: This brings up an interesting
discussion point. Some practitioners
promote not testing “simple” accessor methods, while at the same time placing
considerable business logic within those methods. Or said another way, if a “getter” has ten
lines of logic (on a team committed to testing) – one might be reluctant to
argue an exclusion just because the method name begins with the verb
“get”. If nothing else, TestUtil
provides visibility to so-called accessor methods that are replete with
(business) logic.
The remaining two warnings are additional
occurrences of “setting” a collection but “getting” an element in the collection
(see 11a above). So, the number of
expected "warnings" are asserted to be twelve (12). The unit test will fail until the JavaBeans
Specification violations are resolved.
public
void testAllAccessors_Administration_Presentation() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.administration.presentation",
true,
12);
}
****************************************************************************************************
* TestUtil v. 2.0 - Verify Directory
Summary Results
* -------> Skips: 0 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 308 Successful Verification(s);
12 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 303 Successful Verification(s);
17 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
Run
#11c (administration.common package) Response:
The fourteen (14) “warnings” associated
with this package are a result of entries already made to the testutil-config.xml descriptor document causing Fortune5 method pairs to be excluded
from verification.
// Verification Complete (Green Bar)!
public
void testAllAccessors_Administration_Common() {
TestUtil.verifyDirectory(
"\\C:\\Projects\\WSAD\\Obfuscated\\JavaSource",
false,
"com.obfuscated.administration.common",
true,
14);
}
****************************************************************************************************
*
TestUtil v. 2.0 - Verify Directory Summary Results
* -------> Skips: 22 Non-Java files, interfaces,
or abstract classes not attempted.
* -> Anticipated: 202 Successful Verification(s);
14 Warning(s); 0 Glitch(es); 0 Caution(s)
* -----> Results: 202 Successful Verification(s);
14 Warning(s); 0 Glitch(es); 0 Caution(s)
****************************************************************************************************
Lessons Learned – What went
well?
It was unintentional that individual
developers used verifyMutable()
(exclusively)
and the Consulting Architect used verifyDirectory() (exclusively). This occurred inadvertently due to the
TestUtil product release cycle in relation to the Fortune5 project development cycle.
In retrospect, the approach seems sound.
Individual developers receive immediate feedback on errors within their
coding scope; the Lead Developer or Architect responsible for the health of the
entire code base receives a broader perspective on coding practices followed
and the amount of logic being placed within accessor methods.
Lessons Learned – What could
be improved?
Clearly, the Consulting Architect should
monitor the code base on a timely basis.
Discovery after five (5) occurrences of JavaBeans specification
violations – or ten (10) occurrences of no-parameter “setters” is an indicator
of a lack of attention to overall recommended practice adherence and coding
consistency. This is particularly true
when development is split between on-shore and off-shore and the opportunity
for informal coalescence of coding style is geographically impaired.
Closing Thought
It is becoming apparent to the author the notion of automated method testing relies heavily on a shared understanding and consistent coding practice for a patterned object (DAO, Business Object, Application Service Object etc.). Even the JavaBeans specification at 114 pages does not (unless it was missed :-) address the fundamental real-world stylistic issue of how much transformation and/or functional logic should be placed within accessor methods. The necessity of aligning developer perspectives and coding practices to the extent required for automated method testing to invoke patterned object unit tests – is a feature rather than an encumbrance. If application code is consistent enough for automated method testing – it is certainly consistent enough for effective human readability!
Marvin Toll –
Updated: May, 2006