Bean Matchers
- JavaBeans Spec
- JavaBeans Tutorial
- JavaBeans Wikipedia Entry
- Project Documentation
- Google Code project page (archived)
Beans are prevalent in our Java world, however these simple structures are commonly untested. Over the course of a project they can be inflicted with copy and paste errors, hashCode
, equals
and toString
methods fall out of sync as new properties are added.
Enter Bean Matchers. The Bean Matchers project provides a series of Hamcrest matchers for testing Java beans. This provides an easy way to gain confidence that your beans are correct without needing to consume time writing explicit tests for each property. Ensure a no-args constructor exist. Getters and setters are correctly defined for each property. Properties are compared in equals
, influence the generated hashCode
, and are present in the output of the toString
method.
Special attention has been given to the readability of these matchers. It is important that our tests describe the intent of the code being tested.
Bean Matchers is released under the 3-Clause BSD License.
Quick Start
-
Add the Bean Matchers dependency to your Maven project (Check the Central Repository for the latest version):
<dependency> <groupId>com.google.code.bean-matchers</groupId> <artifactId>bean-matchers</artifactId> <version>0.13</version> <scope>test</scope> </dependency>
-
Test your bean:
import static com.google.code.beanmatchers.BeanMatchers.*; import static org.hamcrest.CoreMatchers.allOf; import static org.hamcrest.MatcherAssert.assertThat; public class MyBeanTest { // One big test @Test public void testBean() { assertThat(MyBean.class, allOf( hasValidBeanConstructor(), hasValidGettersAndSetters(), hasValidBeanHashCode(), hasValidBeanEquals(), hasValidBeanToString() )); } // Individual, well named tests @Test public void shouldHaveANoArgsConstructor() { assertThat(MyBean.class, hasValidBeanConstructor()); } @Test public void gettersAndSettersShouldWorkForEachProperty() { assertThat(MyBean.class, hasValidGettersAndSetters()); } @Test public void allPropertiesShouldInfluenceHashCode() { assertThat(MyBean.class, hasValidBeanHashCode()); } @Test public void allPropertiesShouldBeComparedDuringEquals() { assertThat(MyBean.class, hasValidBeanEquals()); } @Test public void allPropertiesShouldBeRepresentedInToStringOutput() { assertThat(MyBean.class, hasValidBeanToString()); } }
Testing the No-Args Constructor
Ensures a bean has a working no-args constructor.
assertThat(BeanToTest.class, hasValidBeanConstructor());
Testing Getters and Setters
Matchers for ensuring what is stored using a setter is what is obtained using the related getter.
Ensure all properties on the bean have working getters and setters.
assertThat(BeanToTest.class, hasValidGettersAndSetters());
Ensure that the property named testOnlyThisProperty
has a working getter and setter.
assertThat(BeanToTest.class, hasValidGettersAndSettersFor("testOnlyThisProperty"));
Ensure that all properties on the bean except the property named dontTestPropertyWithThisName
has working getters and setters.
assertThat(BeanToTest.class, hasValidGettersAndSettersExcluding("dontTestPropertyWithThisName"));
These matchers test a bean instance.
assertThat(new BeanToTest(), isABeanWithValidGettersAndSetters());
assertThat(new BeanToTest(), isABeanWithValidGettersAndSettersFor("testOnlyThisProperty"));
assertThat(new BeanToTest(), isABeanWithValidGettersAndSettersExcluding("dontTestPropertyWithThisName"));
Testing the hashCode Method
Matchers for ensuring properties influence the generated hash code. This is tested by setting the property with one value and comparing the generated hash code with that produced when the property is set with a different value. If the values differ we infer the hash code is correctly influenced by the property.
Ensure that all properties on the bean influence the produced hash code.
assertThat(BeanToTest.class, hasValidBeanHashCode());
Ensure that the property named propertyInfluencingHashCode
influences the produced hash code.
assertThat(BeanToTest.class, hasValidBeanHashCodeFor("propertyInfluencingHashCode"));
Ensure that all properties on the bean except the property named propertyNotInfluencingHashCode
influences the produced hash code.
assertThat(BeanToTest.class, hasValidBeanHashCodeExcluding("propertyNotInfluencingHashCode"));
Testing the equals Method
Matchers for ensuring properties are compared during the equals operation. Ensure the method accounts for the same instance, a null instance, instance of differing type and null properties.
Ensure all properties on the bean are compared during the equals method.
assertThat(BeanToTest.class, hasValidBeanEquals());
Ensure the property named testOnlyThisProperty
on the bean is compared during the equals method.
assertThat(BeanToTest.class, hasValidBeanEqualsFor("testOnlyThisProperty"));
Ensure all the properties on the bean is compared during the equals method except the property named dontTestPropertyWithThisName
.
assertThat(BeanToTest.class, hasValidBeanEqualsExcluding("dontTestPropertyWithThisName"));
Testing the toString Method
Matchers for ensuring the bean class name and properties are included in toString output.
Ensure all properties on the bean are included in the string value.
assertThat(BeanToTest.class, hasValidBeanToString());
Ensure the string value includes includes the property named propertyInToString
. Any other properties on the bean are not tested.
assertThat(BeanToTest.class, hasValidBeanToStringFor("propertyInToString"));
Ensure the string value includes all properties on the bean except the property named propertyNotInToString
.
assertThat(BeanToTest.class, hasValidBeanToStringExcluding("propertyNotInToString"));
Generating Property Values
The Bean Matchers library generates values to populate beans while performing various tests. Out of the box Bean Matchers will generate random values for properties of primitive, array and enum type. It will delegate to Mockito to create a mock for non-final types. For final types, one can implement and register a ValueGenerator to generate random values.
For example say we have defined a value type:
public final class MyCustomValueType {
private final String value;
public MyCustomValueType(String value) {
this.value = value;
}
...
}
We have a bean with a property of our value type:
public class MyBean {
private MyCustomValueType property;
public MyCustomValueType getProperty() {
return property;
}
public void setProperty(MyCustomValueType property) {
this.property = property;
}
...
}
To test this bean we need to register a ValueGenerator that can generate random values to populate the bean with:
BeanMatchers.registerValueGenerator(new ValueGenerator<MyCustomValueType>() {
public MyCustomValueType generate() {
return new MyCustomValueType(StringUtils.randomString());
}
}, MyCustomValueType.class);