Annotation Interface ProvideMatchers
This annotation is not supported on interface and enum. A error will be generated in this case.
This annotation is processed by an annotation processor, in order to generate :
- One class for each annotated classes, that will contains Hamcrest Matchers for the class.
- In case the annotation processor parameter
"
ch.powerunit.extensions.matchers.provideprocessor.ProvidesMatchersAnnotationsProcessor.factory
" is set, this value define the fully qualified name of a interface that will be generated and will contains all start method allowing to create instance of the various matchers.
The generated classes are related with the hamcrest framework ; This library will be required in the classpath in order to compile or run the generated classes.
Concept regarding the generated Matchers
Hamcrest Matchers can be used, for example, with test framework (JUnit, PowerUnit, etc.) to validate expectation on object. Hamcrest provides several matchers to validate some information of an object (is an instance of, is, an array contains some value, etc.), but can't provide ready to use matcher for your own object. When trying to validate properties of object, no syntaxic sugar (ie. autocompletion) are available and only the generic method can be used.
With this annotation, it is possible to provide builder-like method, based on hamcrest, to validate fields of an object. To do so, the annotation processor do the following :
- For each public field or public method starting with
get
oris
without any argument, generated a private matcher based on theorg.hamcrest.FeatureMatcher
for this field ; this will provide a way to validate the value of one specific property. - Generate an interface and the related implementation of a matcher (which is also a builder) on the annotated classes itself, which will validate all of the properties.
- Generate various methods, with a name based on the annotated class, to start the creation of the matcher.
The processor will also try, for field which are not based on generics, to link the generated matchers between them, in order to provide chaining of the Fields.
First example
Let's assume the following class, containing one single field, will be processed by the annotation processor :
package ch.powerunit.extensions.matchers.samples; import ch.powerunit.extensions.matchers.ProvideMatchers; @ProvideMatchers public class SimplePojo { public String oneField; }In this case a class named
SimplePojoMatchers
will be generated. As a
public interface, the following methods will be available :
public static SimplePojoMatcher simplePojoWith()
: This will return a matcher (see below), which by default matches any instance of the SimplePojo class.-
public static SimplePojoMatcher simplePojoWithSameValue(SimplePojo other)
: This will return a matcher, which by default matches an instance of the SimplePojo having the fieldoneField
matching (Matcheris
of hamcrest) of the reference object.
- Two standards methods are defined for all type of fields :
Matcher <SimplePojo> oneField(Matcher<? super java.lang.String> matcher)
andMatcher<SimplePojo> oneField(String value)
. The second one is a shortcut to validate the field with theis
Matcher and the first one accept another matcher ; The method with matcher parameter ensures that it is possible to combine any other matcher provided by hamcrest or any others extensions. - As the field is a String, others special expectation (shortcut) are
provided, for example :
oneFieldComparesEqualTo
,oneFieldLessThan
,oneFieldStartsWith
, etc.
In case the annotated contains several fields, the generated DSL
provide chained methods, for example
TwoFieldsPojoMatcher firstField(Matcher<? super String> matcher)
and
TwoFieldsPojoMatcher secondField(Matcher<? super String> matcher)
.
Also, depending on the class, other with methods may be provided.
Usage example
Assuming powerunit as a test framework, the usage of the matcher will look like :
@Test public void testOKMatcherWithComparable() { Pojo1 p = new Pojo1(); p.msg2 = "12"; assertThat(p).is(Pojo1Matchers.pojo1With().msg2ComparesEqualTo("12")); }Assuming the
msg2
is change to the value 11
, the resulting
unit test error will look like (the Pojo1 classes contains several fields) :
expecting an instance of ch.powerunit.extensions.matchers.samples.Pojo1 with [msg2 a value equal to "12"] [msg3 ANYTHING] [msg4 ANYTHING] [msg5 ANYTHING] [msg6 ANYTHING] [msg7 ANYTHING] [msg8 ANYTHING] [msg9 ANYTHING] [msg12 ANYTHING] [msg1 ANYTHING] [myBoolean ANYTHING] [oneBoolean ANYTHING] but [msg2 "11" was less than "12"]
Overriding the way the matchers are generated
- The attribute
matchersClassName
may be used to change the simple name (NOT THE FULLY QUALIFIED NAME) of the generated class. - The attribute
matchersPackageName
may be used to change the package name of the generated class.
Extensions
The framework, since version 0.1.0, is able to detect others library and use it :
- If Hamcrest Date is available, additional DSL method are added for the Java 8 Date objects.
- If Hamcrest 1.3 Utility Matchers is available, additional DSL method are added for the collections.
- If Spotify Hamcrest
(jackson) is available and the
JSON_EXTENSION
is used onextensions()
, then method to validate String as json are added. - If Bean Matchers is available additional method to validate Class field.
Linking between matchers
The annotation processor try to link the generated matchers. The goal is that when a field or the super class also have a matchers, this one is used. It is not always possible to link everything but the concept is the following :
- If the super class is compiled at the same time and is annotated with this annotation, the annotation processor may add a control on the parent and generated chaining method.
- If a field class is compiled at the same time and is annotated with this annotation, the annotation processor may add chaining and dedicated control that use this matcher.
- If a matcher, following the convention of this framework is detected for the super class or a field (already compiled for example), the annotation processor may also apply the previous rules by using this detected matcher.
- The annotation processor may also try to find matcher for element of Map, List, Array.
Ignore field in WithSameValue matchers
Since version 0.3.0, it is possible to pass a list of field names to be
ignored when constructing a WithSameValue matcher. The generated
matcher tries to also apply in cascade this ignore feature to included
object. The syntax to ignore fields of another fields is to use
fieldName.fieldNameInside
.
The generated matcher may not be able to apply the ignore feature in all the included fields of the fields.
Cycle detection
Since version 0.3.0, the generated WithSameValue matchers try to detect the cycle in the object to be compared. Cycle may be included for example when working with bidirectional link. The matcher is not able to detect all cycles, but when detected, replace the control by a same instance control.
- Author:
- borettim
-
Optional Element Summary
Modifier and TypeOptional ElementDescriptionboolean
This attribute may be used in case when it is not possible to create a sureWithSameValue
matcher.This attribute may be used to set a comments that will be passed inside thecomments
attribute of the@Generated
annotation.boolean
May be used to disable generation of the factory.String[]
This attribute may be used to enable some extensions for this objects.This attribute may be used to override the default class name that will contains the generated matchers.This attribute may be used to override the default package name that will be used for the generated matchers.This attribute may be used to generate additional exposition methods for the object. -
Field Summary
Modifier and TypeFieldDescriptionstatic final String
May be use as part ofextensions()
to enable, if available, an extension an each String to be validated by using a json validation.
-
Field Details
-
JSON_EXTENSION
May be use as part ofextensions()
to enable, if available, an extension an each String to be validated by using a json validation.- Since:
- 0.1.0
- See Also:
-
-
Element Details
-
matchersClassName
String matchersClassNameThis attribute may be used to override the default class name that will contains the generated matchers.By default, this attribute is an empty string, which indicate to use the default construction pattern.
By default, the Matchers class name is the name of the annotated class, followed by
Matchers
.Using this attribute may make the matcher undetectable when working with separated compilation.
- Returns:
- the name of the matchers class or an empty string if this is not overloaded.
- Default:
- ""
-
matchersPackageName
String matchersPackageNameThis attribute may be used to override the default package name that will be used for the generated matchers.By default, this attribute is an empty string, which indicate to use the default construction pattern.
By default, the Matchers package name is the same that the annotated class.
Using this attribute may make the matcher undetectable when working with separated compilation.
- Returns:
- the name of the matchers package or an empty string if this is not overloaded.
- Default:
- ""
-
comments
String commentsThis attribute may be used to set a comments that will be passed inside thecomments
attribute of the@Generated
annotation.- Returns:
- the comments or an empty string if ignored.
- Default:
- ""
-
moreMethod
ComplementaryExpositionMethod[] moreMethodThis attribute may be used to generate additional exposition methods for the object.By default, only the standard method are generated.
- Returns:
- additional method to be generated or an empty array by default.
- Since:
- 0.1.0
- Default:
- {}
-
extensions
String[] extensionsThis attribute may be used to enable some extensions for this objects.By default, no extension are enabled.
- Returns:
- the extension to be used or an empty array by default.
- Since:
- 0.1.0
- See Also:
- Default:
- {}
-
allowWeakWithSameValue
boolean allowWeakWithSameValueThis attribute may be used in case when it is not possible to create a sureWithSameValue
matcher. This may be the case when the annotated class extends a not annotated class or when the parent class is already compiled.By default, this mode is set to false.
In case this option is set to true and the
WithSameValue
matcher is considered as weak, a warning will be produced.- Returns:
- to create (or not) a
WithSameValue
matcher even if it is not possible to ensure the control of the field of the parent class. - Since:
- 2.0.0
- Default:
- false
-
disableGenerationOfFactory
boolean disableGenerationOfFactoryMay be used to disable generation of the factory.- Returns:
- true to disable the generation of the factory
- Since:
- 0.4.0
- Default:
- false
-