Writing tests
Writing pojo-methods
tests was never so easy. Using POJO-TESTER
you just have to declare what class or classes you want to test and pass it to magic pojo-assertions
. That's all!
Basic pojo test
Basic tests for Pojo class
The simplest pojo test may look like this:
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsFor(classUnderTest).areWellImplemented();
}
It will test a Pojo
class against equals
, hashCode
, toString
, getters
and setters
which is a default test.
If your pojo-methods
are well implemented the test will pass. Otherwise exception will be thrown.
Testing with AssertJ catchThrowable()
If you would rather have strict given-when-then
convention, you can use AssertJ and test may look a little bit better.
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
final Throwable result = Assertions.catchThrowable(() -> assertPojoMethodsFor(classUnderTest).areWellImplemented());
// then
assertThat(result).isNull();
}
But remember, with this solution you will not get precise exception message and you may not know why your pojo-methods
are not well implemented.
Testing by class name
If your class is not public, you cannot access it. Solution for this problem is testing classes via their names:
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_When_Testing_By_Name() {
// given
final String qualifiedClassName = "org.pojo.playground.Pojo";
// when
// then
assertPojoMethodsFor(qualifiedClassName).areWellImplemented();
}
When testing by class name you need to pass fully qualified class name.
Testing with ClassAndFieldPredicatePair
You can pair classes and fields that should be tested in a given class in ClassAndFieldPredicatePair
. This objects is just a facilitation to you:
import pl.pojo.tester.api.ClassAndFieldPredicatePair;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_Using_ClassAndFieldPredicatePair() {
// given
final String qualifiedClassName = "org.pojo.playground.Pojo";
final ClassAndFieldPredicatePair classAndFieldPredicatePair = new ClassAndFieldPredicatePair(qualifiedClassName, FieldPredicate.include("a", "b"));
// when
// then
assertPojoMethodsFor(classAndFieldPredicatePair).areWellImplemented();
}
The code above tests pojo-methods
in the org.pojo.playground.Pojo
class only for fields a
and b
.
Changing nested fields
By default Assertions::assertPojoMethodsFor
performs tests on a given object with changed field values. It uses field value changers to do that (see fields values changer). When it encounters a field that cannot be changed (e.g. CustomPojo
type), it will create a new instance of that type and not perform any changes in this instance. If you want POJO-TESTER
to recursively change values of such a field, you have to pass all classes with their field predicates.
For classes:
class Pojo {
private CustomPojo customPojo;
}
class CustomPojo {
private int a;
}
you have to define test as follows:
@Test
public void Should_Pass_All_Pojo_Tests_Changing_Fields_Recursively() {
// given
final ClassAndFieldPredicatePair baseClass = new ClassAndFieldPredicatePair(Pojo.class, "customPojo");
final ClassAndFieldPredicatePair fieldClasses = new ClassAndFieldPredicatePair(CustomPojo.class, "a");
// when
// then
assertPojoMethodsFor(baseClass, fieldClasses).areWellImplemented();
}
Above test means:
Dear
POJO-TESTER
, when you create different instances of classPojo
, include fieldcustomPojo
, but have in mind that thisCustomPojo
has fielda
. You should generate two instances ofCustomPojo
- each with different value of thea
field, becausePojo::equals
method implementations containscustomPojo
.
Choose kind of tests
There is no need for testing pojo-methods
in a class that don't implemented them.
You can choose which testers you want to run via pl.pojo.tester.api.assertion.AbstractAssetion::testing
method.
Running testers
import pl.pojo.tester.api.assertion.Method;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_Using_All_Testers() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsFor(classUnderTest).testing(Method.GETTER, Method.SETTER, Method.TO_STRING)
.testing(Method.EQUALS)
.testing(Method.HASH_CODE)
.areWellImplemented();
}
Set fields for testing
Next step is excluding
or including
fields which should be tested. By default all the fields are tested.
You can include or exclude fields using pl.pojo.tester.api.FieldPredicate
which creates Java 8 Predicate
that accepts given field names.
Include all fields (default behavior)
import static pl.pojo.tester.api.FieldPredicate.includeAllFields;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_Including_All_Fields() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsFor(classUnderTest, includeAllFields(classUnderTest)).areWellImplemented();
}
Include specified fields
import static pl.pojo.tester.api.FieldPredicate.include;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_Including_Specified_Fields() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsFor(classUnderTest, include("field1", "field2")).areWellImplemented();
}
Exclude spcified fields
import static pl.pojo.tester.api.FieldPredicate.exclude;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests_Excluding_Specified_Fields() {
// given
final Class<?> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsFor(classUnderTest, exclude("field1", "field2")).areWellImplemented();
}
Remember. Always prefer excluding over including as this will make your pojo-tests
less prone to bugs. For example, if you add a new field, but forget to implement equals
method, POJO-TESTER
will catch that. But if you chose to use including
predicate, then you probably also forgot to include that field in your tests.
Configure field value changer
POJO-TESTERS
uses fields values changers
to change field value e.g. when creating different instances.
You can change default fields values changer
via pl.pojo.tester.api.assertion.AbstractAssetion::using
method as shown below.
@Test
public void Should_Pass_All_Pojo_Tests_Using_Custom_Fields_Values_Changer() {
// given
final Class<?> classUnderTest = Pojo.class;
final CustomFieldsValuesChanger customFieldsValuesChanger = new CustomFieldsValuesChanger();
// when
// then
assertPojoMethodsFor(classUnderTest).using(customFieldsValuesChanger)
.areWellImplemented();
}
Define custom fields values changer
To define your own fields values changer
you have to extend pl.pojo.tester.internal.field.AbstractFieldValueChanger
class.
AbstractFieldValueChanger
defines three methods that you have to override:
boolean canChange(final Class<?> type)
- this methods should perform compatibility checks e.g. if class is equal to your changer typeT
. If you decide that value cannot be changed, no further steps are taken. MethodsareDifferentValues
andincreaseValue
are not invoked.boolean areDifferentValues(T sourceValue, T targetValue)
- in this method you have to decide, whether values are different or not. If they are equal no changes will be made. MethodincreaseValue
is not invoked.T increaseValue(T value, final Class<?> type)
- this method should change givenvalue
and return new one.type
is given as little help, when your field type is e.g. interface and the value is its implementation.
Custom fields values changer may look like this:
import pl.pojo.tester.internal.field.AbstractFieldValueChanger;
public class CustomFieldsValuesChanger extends AbstractFieldValueChanger<String> {
@Override
public boolean areDifferentValues(final String sourceValue, final String targetValue) {
return sourceValue.equals(targetValue);
}
@Override
protected boolean canChange(final Class<?> type) {
return type.equals(String.class);
}
@Override
protected String increaseValue(final String value, final Class<?> type) {
return value + "++increased";
}
}
Attaching custom fields values changer
Fields values changer uses chain of responsibility
pattern which allows you to register new fields values changer to default one.
import pl.pojo.tester.internal.field.AbstractFieldValueChanger;
import pl.pojo.tester.internal.field.DefaultFieldValueChanger;
final AbstractFieldValueChanger valueChanger = DefaultFieldValueChanger.INSTANCE.attachNext(customFieldsValuesChanger)
.attachNext(customFieldsValuesChanger)
.attachNext(customFieldsValuesChanger);
Default fields values changer
Default fields values changer is a composition of listed changers:
EnumValueChanger
BooleanValueChanger
ByteValueChanger
CharacterValueChanger
DoubleValueChanger
IntegerValueChanger
LongValueChanger
ShortValueChanger
FloatValueChanger
ArrayValueChanger
StreamValueChanger
ArrayListValueChanger
DequeValueChanger
HashSetValueChanger
LinkedHashSetValueChanger
LinkedListValueChanger
ListValueChanger
QueueValueChanger
SetValueChanger
SortedSetValueChanger
StackValueChanger
TreeSetValueChanger
VectorValueChanger
HashMapValueChanger
HashtableValueChanger
LinkedHashMapValueChanger
MapValueChanger
SortedMapValueChanger
TreeMapValueChanger
IteratorValueChanger
IterableValueChanger
Create class using selected constructor
Sometimes you want to choose which constructor is used to instantiate your class or what parameters are passed. Common example is when constructor validates parameters and throws exceptions.
To indicate what constructor to choose, POJO-TESTER
needs to know three things:
- a class, which constructor will be chosen
- constructor's parameters types
- constructor's parameters
And again, defining this in POJO-TESTER
is a piece of cake:
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests() {
// given
final String qualifiedClassName = "org.pojo.playground.Pojo";
final Object[] constructorParameters = {1, 2.0, new Object()};
final Class[] constructorParameterTypes = {int.class, double.class, Object.class};
// when
// then
assertPojoMethodsFor(qualifiedClassName).create(qualifiedClassName, constructorParameters, constructorParameterTypes)
.areWellImplemented();
}
Here POJO-TESTER
provides additional class, which groups constructor's parameters types and constructor parameters:
import pl.pojo.tester.api.ConstructorParameters;
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsFor;
@Test
public void Should_Pass_All_Pojo_Tests() {
// given
final String qualifiedClassName = "org.pojo.playground.Pojo";
final Object[] parameters = {1, 2.0, new Object()};
final Class[] parameterTypes = {int.class, double.class, Object.class};
final ConstructorParameters constructorParameters = new ConstructorParameters(parameters, parameterTypes);
// when
// then
assertPojoMethodsFor(qualifiedClassName).create(qualifiedClassName, constructorParameters)
.areWellImplemented();
}
Bulk pojos testing
Sometimes you want to test all pojos in one test, e.g. testing toString
method. POJO-TESTER
has feature for testing multiple classes. In order to do that, you have to use Assertions::assertPojoMethodsForAll
instead of Assertions::assertPojoMethodsFor
method:
import static pl.pojo.tester.api.assertion.Assertions.assertPojoMethodsForAll;
@Test
public void Should_Pass_All_Pojo_Tests_For_All_Classes() {
// given
final Class<Pojo> classUnderTest = Pojo.class;
// when
// then
assertPojoMethodsForAll(classUnderTest, classUnderTest, classUnderTest, classUnderTest).areWellImplemented();
}
Method assertPojoMethodsForAll
works a little bit differently than assertPojoMethodsFor
.
This method test all classes. If it encounters field of type from given classes, it will create an instance of that class and change its value recursively.
Imagine you are testing two classes, A
and B
. Class A
has a field of type B
. When you pass those classes to the POJO-TESTER
, it will create instance of B
class, change its value generating different objects, and finally will set all those objects into class A
.