All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hamcrest.beans.HasPropertyWithValue Maven / Gradle / Ivy

There is a newer version: 3.0
Show newest version
/*  Copyright (c) 2000-2006 hamcrest.org
 */
package org.hamcrest.beans;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import org.hamcrest.Condition;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;

import static org.hamcrest.Condition.matched;
import static org.hamcrest.Condition.notMatched;
import static org.hamcrest.beans.PropertyUtil.NO_ARGUMENTS;

/**
 * Matcher that asserts that a JavaBean property on an argument passed to the
 * mock object meets the provided matcher. This is useful for when objects
 * are created within code under test and passed to a mock object, and you wish
 * to assert that the created object has certain properties.
 * 

*

Example Usage

* Consider the situation where we have a class representing a person, which * follows the basic JavaBean convention of having get() and possibly set() * methods for it's properties: *
 * public class Person {
 *   private String name;
 *   public Person(String person) {
 *     this.person = person;
 *   }
 *   public String getName() {
 *     return name;
 *   }
 * }
* * And that these person objects are generated within a piece of code under test * (a class named PersonGenerator). This object is sent to one of our mock objects * which overrides the PersonGenerationListener interface: *
 * public interface PersonGenerationListener {
 *   public void personGenerated(Person person);
 * }
* * In order to check that the code under test generates a person with name * "Iain" we would do the following: *
 * Mock personGenListenerMock = mock(PersonGenerationListener.class);
 * personGenListenerMock.expects(once()).method("personGenerated").with(and(isA(Person.class), hasProperty("Name", eq("Iain")));
 * PersonGenerationListener listener = (PersonGenerationListener)personGenListenerMock.proxy();
* * If an exception is thrown by the getter method for a property, the property * does not exist, is not readable, or a reflection related exception is thrown * when trying to invoke it then this is treated as an evaluation failure and * the matches method will return false. *

* This matcher class will also work with JavaBean objects that have explicit * bean descriptions via an associated BeanInfo description class. See the * JavaBeans specification for more information: *

* http://java.sun.com/products/javabeans/docs/index.html * * @author Iain McGinniss * @author Nat Pryce * @author Steve Freeman */ public class HasPropertyWithValue extends TypeSafeDiagnosingMatcher { private static final Condition.Step WITH_READ_METHOD = withReadMethod(); private final String propertyName; private final Matcher valueMatcher; public HasPropertyWithValue(String propertyName, Matcher valueMatcher) { this.propertyName = propertyName; this.valueMatcher = nastyGenericsWorkaround(valueMatcher); } @Override public boolean matchesSafely(T bean, Description mismatch) { return propertyOn(bean, mismatch) .and(WITH_READ_METHOD) .and(withPropertyValue(bean)) .matching(valueMatcher, "property '" + propertyName + "' "); } @Override public void describeTo(Description description) { description.appendText("hasProperty(").appendValue(propertyName).appendText(", ") .appendDescriptionOf(valueMatcher).appendText(")"); } private Condition propertyOn(T bean, Description mismatch) { PropertyDescriptor property = PropertyUtil.getPropertyDescriptor(propertyName, bean); if (property == null) { mismatch.appendText("No property \"" + propertyName + "\""); return notMatched(); } return matched(property, mismatch); } private Condition.Step withPropertyValue(final T bean) { return new Condition.Step() { @Override public Condition apply(Method readMethod, Description mismatch) { try { return matched(readMethod.invoke(bean, NO_ARGUMENTS), mismatch); } catch (Exception e) { mismatch.appendText(e.getMessage()); return notMatched(); } } }; } @SuppressWarnings("unchecked") private static Matcher nastyGenericsWorkaround(Matcher valueMatcher) { return (Matcher) valueMatcher; } private static Condition.Step withReadMethod() { return new Condition.Step() { @Override public Condition apply(PropertyDescriptor property, Description mismatch) { final Method readMethod = property.getReadMethod(); if (null == readMethod) { mismatch.appendText("property \"" + property.getName() + "\" is not readable"); return notMatched(); } return matched(readMethod, mismatch); } }; } /** * Creates a matcher that matches when the examined object has a JavaBean property * with the specified name whose value satisfies the specified matcher. *

* For example: *

assertThat(myBean, hasProperty("foo", equalTo("bar"))
* * @param propertyName * the name of the JavaBean property that examined beans should possess * @param valueMatcher * a matcher for the value of the specified property of the examined bean */ @Factory public static Matcher hasProperty(String propertyName, Matcher valueMatcher) { return new HasPropertyWithValue(propertyName, valueMatcher); } }