sheat2500 / hamcrest

Automatically exported from code.google.com/p/hamcrest
0 stars 0 forks source link

Use of expressions to check "hasProperty" in beans #199

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Hi,

I would like to see an enhancement to matchers for beans so that they can 
support "expressions" like OGNL 
(http://commons.apache.org/proper/commons-ognl/) or Spring EL 
(http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html). 

Motivation:
In a complex POJO, the usage of expressions could allow to test more easily 
complex POJOs. Syntax would look like the following:

    assertThat(myBean, hasPropertyByExp(expression, valueMatcher)

The matcher is similar to hasProperty(String propertyName, Matcher<?> 
valueMatcher) but here the  "expression" parameter is a String that represents 
a full expression which allow querying the object graph of the bean.

As a result, asserts could be more compact and so easier to read and understand 
(see the following example).

Example:

Suppose MyBean is a class with a Map<String,MyOtherBean> field named "map" and 
MyOtherBean is a class with a List<String> field named list. And it is 
populated like the following:

    Mybean myBean = new MyBean();
    myBean.setMap(new HashMap());
    myBean.getMap().put("myKey", new MyOtherBean());
    myBean.getMap().get("myKey").setList(Arrays.asList("one", "two", "three"));

then, if you want to test if "three" is equal to the third element of the 
"list" of the instance of MyOtherBean stored under the key "myKey" in the map 
"map" of the object "myBean",  you could write a single line assert in the 
following way:

    assertThat(myBean, hasPropertyByExp("map['myKey'].list[3]", equalsTo("three"));

Technology:
I actually use Spring EL to write tests based on expression on beans. I 
retrieve the value using an expression and I simply compare it with 
"assertEquals".

I've written the following utility static method based on Spring EL:

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

//...

  public static <T> T getValue(Object pojo, String expression, Class<T> t) {
    ExpressionParser parser = new SpelExpressionParser();
    Expression exp = parser.parseExpression(expression);
    return exp.getValue(pojo, t);
  }

If the test is embedded in a matcher, it would make the code even more readable.

General considerations to be done before implementation:
It should be considered the impact of dependencies using either Spring EL or 
OGNL or other expression languages (my choice of using Spring EL has been taken 
due to the fact I already have Spring dependencies on my code). 

Considering that Spring EL is made also for manipulating the Object Graph, and 
for testing purposes only query of the object graph is relevant, it could be 
feasible also to implement a limited Expression Language specifically for 
Hamcrest, and avoid external dependencies. 

The following link points to the Spring EL syntax reference: 
http://static.springsource.org/spring/docs/3.0.x/reference/expressions.html#expr
essions-language-ref

In particular, the most meaningful language feature of the Spring EL for 
writing matchers is the syntax for querying "Properties, Arrays, Lists, Maps, 
Indexers" as is described in section 6.5.2 of the previous link. Maybe this 
could be implemented in Hamcrest with a little reflection and a good regex to 
parse the expression.

Final note:
I've searched this feature in Hamcrest and other frameworks without success, if 
I've missed it, please don't blame me and please give me a link to the 
solution. I've tried to write the most precise report for the enhancement 
request, if there is something which is not clear, please ask clarification in 
the comments. 

Thank you.
Cristiano

Original issue reported on code.google.com by cristian...@gmail.com on 28 May 2013 at 1:51

GoogleCodeExporter commented 9 years ago

Original comment by t.denley on 28 May 2013 at 10:53

GoogleCodeExporter commented 9 years ago
Cristiano,

In my own search for a similar feature today I found I was able to satisfy my 
requirement via a combination of the org.hamcrest.beans.HasPropertyWithValue 
matcher and nested org.hamcrest.Matchers matchers.

For your example above, I came up with two possible assertThat's that may 
satisfy your requirement, depending exactly what you need to check in the list 
sub-property.

First idea:

        Assert.assertThat(
                "Expected myBean.map['myKey'].list to equal [\"one\", \"two\", \"three\"]",
                myBean,
                HasPropertyWithValue.<MyBean> hasProperty(
                        "map",
                        IsMapContaining.<String, MyOtherBean> hasEntry(
                                Matchers.equalTo("myKey"),
                                HasPropertyWithValue.<MyOtherBean> hasProperty("list",
                                        Matchers.contains("one", "two", "three")))));

Second idea - perhaps closer to your original pseudocode's intent:

        // assertThat(myBean, hasPropertyByExp("map['myKey'].list[3]", Matchers.equalTo("three")));
        Assert.assertThat(
                "Expected myBean.map['myKey'].list[3] to equal \"three\"",
                myBean,
                HasPropertyWithValue.<MyBean> hasProperty(
                        "map",
                        IsMapContaining.<String, MyOtherBean> hasEntry(
                                Matchers.equalTo("myKey"),
                                HasPropertyWithValue.<MyOtherBean> hasProperty(
                                        "list",
                                        Matchers.contains(Matchers.anything(), Matchers.anything(),
                                                Matchers.equalTo("three"))))));

Does either of these do the job well enough for your need?

Hopefully putting the above in the public record helps us and other future 
travellers of this Java testing path. :)

Mark

Original comment by mark.a.f...@gmail.com on 27 Jan 2015 at 8:44