Automatic hamcrest matcher for model classes for Java 11
Writing a hamcrest matcher for your model classes by extending TypeSafeDiagnosingMatcher
is a good idea, because it gives you a readable diff of actual and expected property values. But doing it by hand is tedious and hard to get right, especially for classes with many properties:
- It is easy to make mistakes in the
matches()
method. - It requires lot's of boiler plate code.
- Good layout of actual and expected property values is hard to get right.
- Each property occurs in multiple places which violates the DRY principle.
- Changelog
- Requirements
- Java 11
repositories {
mavenCentral()
}
dependencies {
testCompile 'org.itsallcode:hamcrest-auto-matcher:0.7.0'
}
<dependency>
<groupId>org.itsallcode</groupId>
<artifactId>hamcrest-auto-matcher</artifactId>
<version>0.7.0</version>
<scope>test</scope>
</dependency>
Using AutoMatcher
Assume you have two model classes DemoModel
and DemoAttribute
:
public class DemoModel {
private final int id;
private final String name;
private final DemoAttribute attr;
private final List<DemoModel> children;
private final String[] stringArray;
private final Long longVal;
public DemoModel(int id, String name, Long longVal, DemoAttribute attr, String[] stringArray,
List<DemoModel> children) {
this.id = id;
this.name = name;
this.longVal = longVal;
this.attr = attr;
this.stringArray = stringArray;
this.children = children;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public DemoAttribute getAttr() {
return attr;
}
public List<DemoModel> getChildren() {
return children;
}
public String[] getStringArray() {
return stringArray;
}
public Long getLongVal() {
return longVal;
}
@Override
public String toString() {
return "DemoModel [id=" + id + ", name=" + name + ", attr=" + attr + ", children=" + children + ", stringArray="
+ Arrays.toString(stringArray) + ", longVal=" + longVal + "]";
}
}
public class DemoAttribute {
private final String value;
public DemoAttribute(String value) {
this.value = value;
}
public String getValue() {
return value;
}
@Override
public String toString() {
return "DemoAttribute [value=" + value + "]";
}
}
Use AutoMatcher.equalTo()
to create a matcher for your expected model instance. This will use reflection to determine expected property values based on getter methods:
org.itsallcode.matcher.auto.AutoMatcher;
DemoModel expected = ...;
DemoModel actual = ...;
assertThat(actual, AutoMatcher.equalTo(expected));
Example mismatch report:
Expected: {id=<4711>, longVal=null, name="name1", attr=null, stringArray=null, children=null}
but: {longVal was <42L>}
AutoMatcher creates properties for methods matching the following criteria:
- Visibility:
public
- Name: starts with
get
oris
- Signature: not
void
and no arguments - Not one of the built-in methods
getClass()
,getProtectionDomain()
,getClassLoader()
,getURLs()
Using ConfigurableMatcher
If AutoMatcher
does not work for your model classes, you can still use ConfigurableMatcher
and MatcherConfig
which allows you to specify properties and custom matchers explicitly but is much easier to use than TypeSafeDiagnosingMatcher
.
public class DemoModelMatcher {
public static Matcher<DemoModel> equalTo(DemoModel expected) {
final MatcherConfig<DemoModel> config = MatcherConfig.builder(expected)
.addEqualsProperty("id", DemoModel::getId)
.addEqualsProperty("longVal", DemoModel::getLongVal)
.addEqualsProperty("name", DemoModel::getName)
.addProperty("attr", DemoModel::getAttr, DemoAttributeMatcher::equalTo)
.addEqualsProperty("stringArray", DemoModel::getStringArray)
.addIterableProperty("children", DemoModel::getChildren, DemoModelMatcher::equalTo)
.build();
return new ConfigurableMatcher<>(config);
}
}
Also see DemoModelMatcher
as an example.
git clone https://github.com/itsallcode/hamcrest-auto-matcher.git
cd hamcrest-auto-matcher
./gradlew check
# Test report: build/reports/tests/index.html
Import into eclipse using buildship.
./gradlew clean sonar --info -Dsonar.token=[token]
./gradlew dependencyUpdates
To calculate and view test coverage:
./gradlew check jacocoTestReport
open build/reports/jacoco/test/html/index.html
-
Add the following to your
~/.gradle/gradle.properties
:ossrhUsername=<your maven central username> ossrhPassword=<your maven central passwort> signing.keyId=<gpg key id (last 8 chars)> signing.password=<gpg key password> signing.secretKeyRingFile=<path to secret keyring file>
-
Increment version number in
build.gradle
andREADME.md
, updateCHANGELOG.md
, commit and push. -
Optional: run the following command to do a dry-run:
./gradlew clean check build publishToSonatype closeSonatypeStagingRepository --info
-
Run the following command to publish to Maven Central:
./gradlew clean check build publishToSonatype closeAndReleaseSonatypeStagingRepository --info
-
Create a new release on GitHub.
-
After some time the release will be available at Maven Central.