The purpose of this module is to 'automatically' test the basics of java classes such as getters and setters, constructors, equals and hashcode methods and other basic compliency rules.
Other purpose is to have all this code covered without hard work.
After having done a small configuration of your project, you simply have to annotate an ObjectValidator with @ObjectValidationRules and tell him which classes he has to verify
For this to be used you need a basic understanding on how unitils works.
Simply put, a unitils.properties files has to be found at the root of test/resources folder of the project you want to test,
and annotate your test class with @RunWith(UnitilsJUnit4TestClassRunner.class).
This additional maven dependency has to be added :
<dependency> <groupId>org.unitils.objectvalidation</groupId> <artifactId>unitils-objectvalidation</artifactId> <version>1.1.4</version> </dependency>
Please create unitils-local.properties, and add objectvalidation to unitils.modules. Code as following:
unitils.modules=objectvalidation unitils.module.objectvalidation.className=org.unitils.objectvalidation.ValidationModule unitils.module.objectvalidation.runAfter= unitils.module.objectvalidation.enabled=true
This first example tests three objects and checks their equals and hashcode method
@RunWith(UnitilsJUnit4TestClassRunner.class) public class ValidatesBeanWithAnnotationTest { @ObjectValidationRules private ObjectValidator objectValidator; @Test public void validateBeans() { objectValidator. classToValidate(ValidBean.class).checkingAllPossibilities().withAllFields(). classToValidate(ValidBeanWithByteArray.class).checkingAllPossibilities().withFieldNames("firstField", "secondField", "byteArray"). classToValidate(ValidBeanOnlyId.class).checkingAllPossibilities().withFieldNames("id"). validate(); } }
This example is to show how to test a simple utility method
@RunWith(UnitilsJUnit4TestClassRunner.class) public class ValidatesUtilityClassWithAnnotationTest { @ObjectValidationRules(replacementRules = {UtilityClassRules.class}) private ObjectValidator utilityValidator; @Test public void validateUtilsClass() { utilityValidator. classToValidate(Utils.class). withoutCheckingAllPossibilities(). validate(); } }
Out of the box it exists two basic rule collection :
SunBeanRules, it will check that :
That there is a public empty constructor
org.unitils.objectvalidation.rules.collection=org.unitils.objectvalidation.rulescollection.SunBeanRules
SunBeanRulesWithMocks does almost the same as the SunBeanRules, but it won't generate the entire object graph. It creates an object with primitives and mocks.
org.unitils.objectvalidation.rules.collection=org.unitils.objectvalidation.rulescollection.SunBeanRulesWithMocks
UtilityClassRules, it will check that :
That the class is final
org.unitils.objectvalidation.rules.collection=org.unitils.objectvalidation.rulescollection.UtilityClassRules
You can choose to use or not the equals and hashCode validator. If you want to have maximum coverage on your overriden equals and hashCode methods then it's advised to use it.
If you want to validate the class equals and hashCode method, you need to implement it this way :
objectValidator.classToValidate(ValidBean.class). checkingAllPossibilities(). withAllFields(). validate();
This will take class ValidBean and create a different object per field and compare them against each other to see if the equals and hashCode method are complient.
If you don't want to validate it (for example in the case of an utility method) :
objectValidator.classToValidate(Utils.class). withoutCheckingAllPossibilities(). validate();
What if in your class Person you have a simple identity equals and don't look at other fields ?
objectValidator.classToValidate(Person.class). checkingAllPossibilities(). withFieldNames("id"). validate();
You can also add several classes at the same time :
objectValidator. classToValidate(ValidBean.class).checkingAllPossibilities().withAllFields(). classToValidate(ValidBeanWithByteArray.class).checkingAllPossibilities().withAllFields(). classToValidate(ValidBeanOnlyId.class).checkingAllPossibilities().withFieldNames("id"). validate();
For the objectvalidation being able to work it exists an ObjectCreator that will create randomized instance of classes.
They are defined inside the RulesCollection, it's a field. For the SunBeanRules for example the org.unitils.objectvalidation.objectcreator.ObjectCreatorFactory.createDefaultObjectCreator() is used.
If you use something else than the default types of object (for example use joda.time) then you need to override this objectCreator like this :
private static ObjectCreator createProjectObjectCreator() { return ObjectCreatorFactory.createObjectCreator(new Generator[] {new ProjectCompositeGenerator()}); } public static class ProjectCompositeGenerator extends CompositeGenerator { public ProjectCompositeGenerator() { addGenerators(new Generator[] {new JodaTimeGenerator(), new EnumGenerator(), new PrimitiveGenerator(), new CollectionGenerator(), new BuilderGenerator(), new LastResortGenerator()}); } }
The jodaTimeGenerator must implement org.unitils.objectvalidation.objectcreator.generator.Generator and generate random objects.
public static class JodaTimeGenerator implements Generator { /** * Returns now. */ @Override public Object generateObject(Class<?> clazz, List<Object> input, List<Class<?>> inputClasses, List<TreeNode> genericSubTypes) throws Exception { if (DateTimeZone.class.equals(clazz)) { return DateTimeZone.getDefault(); } else if (DateTime.class.equals(clazz)) { return DateTime.now(); } return null; }
If in your project you have specific rules that you want to test ? Check that on each class you have overriden the toString method for example ?
You simply create a class implementing org.unitils.objectvalidation.Rule like this :
public class ToStringHasToBeOverridenRule implements Rule { @Override public void validate(Class<?> classToValidate) { Method toStringMethod; try { toStringMethod = classToValidate.getDeclaredMethod("toString"); assertNotNull("toString method has to be Overriden", toStringMethod); } catch (SecurityException e) { fail("Could not be read due to security reasons.\n" + e.getMessage()); } catch (NoSuchMethodException e) { fail("Does not contain toString method. It should be overriden."); } } }
If the rules collection SunBeanRules and UtilityClassRules don't fit your purpose you simply can redefine your rule collection by implementing org.unitils.objectvalidation.ObjectValidationRulesCollection :
You then need to override the method public List<Rule> getRules(), provide a list of rules for your project, for instance :
@Override public List<Rule> getRules() { return Arrays.asList(new EqualsComplientRule(objectCreator, objectCloner) , new HashCodeComplientRule(objectCreator, objectCloner), new GetterRctComplientRule(objectCreator, objectCloner), new NoSetterRule(), new PrivateConstructorWithBuilderRule(), new ToStringHasToBeOverridenRule()); }
You see here the use of the objectCreator (see 6. in this document) and the object cloner, you can use the default ones :
public ProjectRules() { objectCreator = ObjectCreatorFactory.createDefaultObjectCreator(); objectCloner = ObjectClonerFactory.createDefaultCloner(); }
Or define your own.
Then you have to override the method public EqualsHashCodeValidator getEqualsHashCodeValidator()
Where you can use the default one :
return new EqualsHashCodeValidator(objectCreator, objectCloner);
or redefine your own.
If on a project you need for one or several test to don't use the specified rule collection (in the unitils.properties file) and use another instead you can define that at annotation time :
@ObjectValidationRules(replacementRules = SunBeanRules.class)
If you want to run the configured one (in the unitils.properties file) and also another set of rules in addition use this parameter of the annotation :
@ObjectValidationRules(additionalRules = SunBeanRules.class)