
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 refactor
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 &
refactor 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, best practices for the TestUtil verifyMutable() method are inferred. In May of 2006, the Chronicle was updated to
include capability 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 layer similar to Joppa.
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) best 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 best 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 serviceability!
Marvin Toll –
Updated: May, 2006