I often get the question if it is necessary to test simple things as Pojo's or javabeans? The reason I get the question is that those objects are nothing more than simple classes with properties and their necessary accessor methods, respecting the sun javabean specification API. That's correct but how do we know the implementation respects the javabean specification? Right, we test it. In this section we'll show you a generic way to verify that your javabeans / pojo's respect the conventions by sun. We'll start by briefly explaining javabeans and pojo's.
A javabean is a reusable component that encapsulates other objects, its properties, so that they can be passed around as a single bean object instead of as multiple individual objects. In order to function as a JavaBean class, an object class must obey certain conventions about method naming, construction, and behavior. These conventions make it possible to have tools that can use, reuse, replace, and connect JavaBeans.
The required conventions are:
POJOs (Plain Old Java Objects) resemble to javabeans but have less strict conventions to respect. They don't have to be serializable and a default constructor is not mandatory. Because POJOs do not require to adhere to specific external interfaces or third-party APIs it decouples the code from external associations. One of the primary benefits of this decoupling is the freedom it gives software developers from ancillary tasks, such as persistence, transaction support, and remoting.
POJOs are often used in a Service Oriented Architecture, they represent the anemic domain objects. All the business logic is embedded in the services.
When we want to use Pojo's, for example to represent the anemic domain objects, or we use a set of javabeans we want to be sure that the implementation respects the conventions. Writing a test for each object would mean a serious overhead, so let's try something else.
If you are using maven, you can add following dependency to your project.
01 02 03 04 05 | < dependency > < groupId >org.unitils.objectvalidation</ groupId > < artifactId >unitils-objectvalidation</ artifactId > < version >1.1.9</ version > </ dependency > |
Please create unitils-local.properties, and add mail to unitils.modules. Code as following:
01 02 03 04 05 06 07 08 09 | unitils.modules =objectvalidation unitils.module.objectvalidation.className =org.unitils.objectvalidation.ValidationModule unitils.module.objectvalidation.runAfter = unitils.module.objectvalidation.enabled =true #Optional: org.unitils.objectvalidation.rules.collection = ??? #You can give the name of the RulesCollection that will be used by default. default: SunBeanRules . |
Rule | A rule is a java class which purpose is to validate something on another class, it could be verify that the toString method is well written and overriden for instance. |
RuleCollection | Is a set of rules used as configuration of your project. Out of the box there is the SunBeanRules that verifies that objects are written with Sun compliency. |
EqualsHashCodeValidator | Utility that will check that the equals and hashCode uses all the specified fields, it will cover the conditions of the equals and hashCode totally. |
By default there are 2 types of rulescollections:
When a testobject is created, then it will look for a field that is annotated with @ObjectValidationRules. You can alter the rulescollection by adding or replacing rules.
@ObjectValidationRules(replacementRules = {UtilityClassRules.class}, additionalRules={ExtraRules.class} ) private ObjectValidator objectValidator;
@ObjectValidationRules private ObjectValidator objectValidator; @Test public void validatePersonBean() { objectValidator.classToValidate(PersonBean.class).checkingAllPossibilities().withAllFields().validate(); }
In the previous example we want to validate the PersonBean. So with this info the objectvalidator can create a ValidateEqualsAndHashCode where have the option to validate it with or without equals and hashcode. But in the previous example we choose to validate everything: equals, hashcode and all the fields.
ObjectValidator | This is created by the module and contains all the rules that you want to use. |
ValidateEqualsAndHashCode | Gives us the option to validate the equals and hashcode too or not. |
ChooseFieldsInEquals | 3 options: ignoringFields, withFieldNames, withAllFields |
AddClassToValidate | * validate(): validates all the classes that you have added to the ObjectValidator * classToValidate(Class? clzz): You can add another class and this method will return a ValidateEqualsAndHashCode object |
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @RunWith (UnitilsJUnit4TestClassRunner. class ) public class ObjectValidationExample1 { @ObjectValidationRules private ObjectValidator objectValidator; /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { } @Test public void test() { objectValidator. classToValidate(ValidBean. class ).checkingAllPossibilities().withAllFields(). classToValidate(ValidBeanWithByteArray. class ).checkingAllPossibilities().withAllFields(). classToValidate(ValidBeanOnlyId. class ).checkingAllPossibilities().withFieldNames( "id" ). validate(); } } |
A generator is an object that creates random objects of a specific type. Everything is already in place for basic validation (f.e.: the validation of primitives, collections, enums\ldots). But if you need something more specific, like validation of jodatime, than you need to write it yourself.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 | public class TimeZoneGenerator implements Generator { private static final List<String> ZONES = Arrays.asList(TimeZone.getAvailableIDs()); /** * @see org.unitils.objectvalidation.objectcreator.generator.Generator#generateObject(java.lang.Class, java.util.List, java.util.List, java.util.List) */ public Object generateObject(Class<?> clazz, List<Object> input, List<Class<?>> inputClasses, List<TreeNode> genericSubTypes) throws Exception { if (clazz.equals(TimeZone. class )) { //get random time zone Random r = new Random(); int zone = r.nextInt(ZONES.size()- 1 ); return new SimpleTimeZone(r.nextInt( 100 ), ZONES.get(zone)); } return null ; } } |
Every RulesCollections contains a list with rules. You can create your own rules by implementing the interface Rule and by adding them to the RuleCollections.
01 02 03 04 05 06 07 08 09 10 | public class ToStringHasToBeOverridenRule implements Rule { /** * @see org.unitils.objectvalidation.Rule#validate(java.lang.Class) */ public void validate(Class<?> classToValidate) { // Do something } } |