ch.powerunit.extensions.matchers.ProvideMatchers Maven / Gradle / Ivy
Show all versions of powerunit-extensions-matchers-factory Show documentation
/**
* Powerunit - A JDK1.8 test framework
* Copyright (C) 2014 Mathieu Boretti.
*
* This file is part of Powerunit
*
* Powerunit is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Powerunit is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Powerunit. If not, see .
*/
package ch.powerunit.extensions.matchers;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation can be used on a java class, to mark this class as supporting
* generation of hamcrest matcher.
*
* This annotation is not supported on interface and enum. A warning will be
* displayed 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
* "{@code 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 {@code get} or
* {@code is} without any argument, generated a private matcher based on the
* {@link org.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 annotation processor will also generate javadoc and try to retrieve from
* the javadoc of the annotated element the information regarding generic
* attribute.
*
* 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 {@code SimplePojoMatchers} will be generated. As a
* public interface, the following methods will be available :
*
* - {@code public static SimplePojoMatcher simplePojoWith()}: This will
* return a matcher (see below), which by default matches any instance of the
* SimplePojo class.
* -
* {@code public static SimplePojoMatcher simplePojoWithSameValue(SimplePojo other)}
* : This will return a matcher, which by default matches an instance of the
* SimplePojo having the field {@code oneField} matching (Matcher {@code is} of
* hamcrest) of the reference object.
*
* The returned interface is already a correct hamcrest matcher. This interface
* provide method that set the expectating on the various fields. As in this
* case, where is only one field, the returned interface ensure that once the
* expected is defined, it is not possible to modify it. Depending of the type
* of the field, various methods are generated to define the expectation :
*
* - Two standards methods are defined for all type of fields : {@code Matcher
*
oneField(Matcher super java.lang.String> matcher)} and
* {@code Matcher oneField(String value)}. The second one is a
* shortcut to validate the field with the {@code is} 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 : {@code oneFieldComparesEqualTo},
* {@code oneFieldLessThan}, {@code oneFieldStartsWith}, etc.
*
* Second example
*
* In case the annotated contains several fields, the generated DSL
* provide chained methods, for example
* {@code TwoFieldsPojoMatcher firstField(Matcher super String> matcher)} and
* {@code 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 {@code msg2} is change to the value {@code 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 {@link #matchersClassName() matchersClassName} may be used
* to change the simple name (NOT THE FULLY QUALIFIED NAME) of the
* generated class.
* - The attribute {@link #matchersPackageName() 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 {@link #JSON_EXTENSION} is used on
* {@link #extensions()}, then method to validate String as json are added.
* - If Bean Matchers is
* available additional method to validate Class field.
*
*
* @author borettim
*
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE })
@Inherited
public @interface ProvideMatchers {
/**
* This 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 {@code Matchers}.
*
* @return the name of the matchers class or an empty string if this is not
* overloaded.
*/
String matchersClassName() default "";
/**
* This 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.
*
* @return the name of the matchers package or an empty string if this is
* not overloaded.
*/
String matchersPackageName() default "";
/**
* This attribute may be used to set a comments that will be passed inside
* the {@link javax.annotation.Generated#comments() comments} attribute of
* the {@link javax.annotation.Generated @Generated} annotation.
*
* @return the comments or an empty string if ignored.
*/
String comments() default "";
/**
* This attribute may be used to generate additional exposition methods for
* the object.
*
* By default, only the standard method are generated.
*
* @return additional method to be generated or an empty array by default.
* @since 0.1.0
*/
ComplementaryExpositionMethod[]moreMethod() default {};
/**
* This attribute may be used to enable some extensions for this objects.
*
* By default, no extension are enabled.
*
* @return the extension to be used or an empty array by default.
* @since 0.1.0
* @see #JSON_EXTENSION An extension to add a validation on String, as JSON,
* based on the
* Spotify
* Matchers.
*/
String[]extensions() default {};
/**
* May be use as part of {@link #extensions()} to enable, if available, an
* extension an each String to be validated by using a json validation.
*
*
* @since 0.1.0
*/
public static final String JSON_EXTENSION = "json-extension";
}