Constructing objects from table parameters

834 views
Skip to first unread message

insitu

unread,
Sep 16, 2011, 10:14:46 AM9/16/11
to Cukes
Hello,
I am using cucumber-jvm and wrote the following fixture:

Feature: Indexer Service
In order to limit reindexation
IndexerService will need to retrieve changed documents periodically

Scenario: Retrieve Added Documents
Given documents database contains:
| Uri | Created Time | Type |
| doc1 | 0 | type1 |
| doc2 | 0 | type2 |
When getting changed since time 0
Then I retrieve 2 documents


Then my Steps definition file looks like:


public class IndexerServiceSteps {

public static class DocumentInfo {
public String uri;
public int createdTime;
public String type;

public Document asDocument() {
Document currentDocument = new Document();
currentDocument.setUri(uri);
currentDocument.setCreatedTime(new Date(createdTime));
currentDocument.setType(type);
return currentDocument;
}
}

private static Set<Document> store = new HashSet<Document>();

@Given("^documents database contains:$")
public void documentsDatabaseContains(List<DocumentInfo> documents)
{
for (DocumentInfo documentInfo : documents) {
store.add(documentInfo.asDocument());
}
}

@When("^getting changed since time (\\d)$")
public void gettingSinceTime(int since) {
...
}

@Then("^I retrieve (\\d+) documents$")
public void retrieveDocuments(int expectedNumberRetrieved) {
...
}

}


I do not like much defining a DocumentInfo for wrapping data around a
Document so would rather build a Document directly. But this object
only supports setter-based DI.
Is there something I can do?

Thanks
Arnaud

aslak hellesoy

unread,
Sep 16, 2011, 10:19:02 AM9/16/11
to cu...@googlegroups.com
What error are you getting when you replace the stepdef arg with List<Document>?

Aslak

Thanks
Arnaud

--
You received this message because you are subscribed to the Google Groups "Cukes" group.
To post to this group, send email to cu...@googlegroups.com.
To unsubscribe from this group, send email to cukes+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/cukes?hl=en.


insitu

unread,
Sep 16, 2011, 11:14:20 AM9/16/11
to Cukes
I got the following stacktrace. There is a setUri() method in
Document.

Given documents database contains:(Scenario: Retrieve Added
Documents) Time elapsed: 0.167 sec <<< ERROR!
com.thoughtworks.xstream.converters.ConversionException: Element uri
of type java.net.URI is not defined as field in type
com.polyspot.iw.business.model.Docu
ment
---- Debugging information ----
class : com.polyspot.iw.business.model.Document
required-type : com.polyspot.iw.business.model.Document
converter-type :
com.thoughtworks.xstream.converters.reflection.ReflectionConverter
path : /list/com.polyspot.iw.business.model.Document/
uri
class[1] : java.util.ArrayList
converter-type[1] :
com.thoughtworks.xstream.converters.collections.CollectionConverter
version : null
-------------------------------
at
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.writeValueToImplicitCollection(AbstractReflectionConverter.java:
399)
at
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:
330)
at
com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:
230)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:
72)
at
com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:
65)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
66)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
50)
at
com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readItem(AbstractCollectionConverter.java:
71)
at
com.thoughtworks.xstream.converters.collections.CollectionConverter.addCurrentElementToCollection(CollectionConverter.java:
79)
at
com.thoughtworks.xstream.converters.collections.CollectionConverter.populateCollection(CollectionConverter.java:
72)
at
com.thoughtworks.xstream.converters.collections.CollectionConverter.populateCollection(CollectionConverter.java:
66)
at
com.thoughtworks.xstream.converters.collections.CollectionConverter.unmarshal(CollectionConverter.java:
61)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:
72)
at
com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:
65)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
66)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:
50)
at
com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:
134)
at
com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:
32)


On Sep 16, 4:19 pm, aslak hellesoy <aslak.helle...@gmail.com> wrote:

aslak hellesoy

unread,
Sep 16, 2011, 12:46:51 PM9/16/11
to cu...@googlegroups.com
Cucumber-JVM uses XStream to convert a table to a List of objects. By default, XStream uses its ReflectionConverter class to set fields on an object. It does this directly on the fields without invoking any setters. (This also works with private fields - the reflection is very clever).

XStream has another converter - JavaBeanConverter, which sets fields using setters (according to the JavaBean spec). This would do what you want. It's not enabled in XStream by default. We could enable it, but that has some side-effects:

Enabling JavaBeanConverter would cause the ReflectionConverter to stop working. I haven't figured out a way to make them coexist. So here is my proposal:

Cucumber-JVM will not try to initialie objects as beans by default (because java beans suck in general - setters are evil, but that's another story). However, if you *really* want to use setters, we could provide a mechanism where you tell Cucumber-JVM to use them:

public void documentsDatabaseContains(@JavaBeans List<Document> documents)

or perhaps:

public void documentsDatabaseContains(@Converter(JavaBeanConverter.class) List<Document> documents)

I'll have to play around with it a little.

Arnaud Bailly

unread,
Sep 16, 2011, 1:05:59 PM9/16/11
to cu...@googlegroups.com
Hi aslak,
Thanks a lot for taking the time to analyze this issue. Actually, I am definitely on line with your feelings about Javabeans and setters: They clearly suck and mutable state should be avoided if at all possible. Unfortunately, there exists quite a bunch of lines of code who do not feel that way and thinks Javabeans are cool and we should stick with this convention, and a lot of tools think the same way. I even myself indulge sometimes in this setter thing, for example when using spring as DI container.

I am ok with sticking with wrapper objects at the moment. However it might be interesting to have a general mechanism to allow users to use their own converters when constructing objects from tables. I say "might" because I think it is mixed blessing: It would open the door to arbitrary instantiation of complex objects which goes against the general trend towards greater simplicity in code that cucumber and BDD promotes.

Regards,
Arnaud

aslak hellesoy

unread,
Sep 16, 2011, 2:25:45 PM9/16/11
to cu...@googlegroups.com
On Fri, Sep 16, 2011 at 6:05 PM, Arnaud Bailly <arnaud...@gmail.com> wrote:
Hi aslak,
Thanks a lot for taking the time to analyze this issue. Actually, I am definitely on line with your feelings about Javabeans and setters: They clearly suck and mutable state should be avoided if at all possible. Unfortunately, there exists quite a bunch of lines of code who do not feel that way and thinks Javabeans are cool and we should stick with this convention, and a lot of tools think the same way. I even myself indulge sometimes in this setter thing, for example when using spring as DI container.


<digression>
My favourite DI container is the "new" keyword ;-). Number 2 is PicoContainer (which I co-wrote 8 years ago, and still holds up nicely, powering IntelliJ IDEA's plugins and Cucumber-JVM).
Spring has become what it initially set out to replace because it was so complex (J2EE).
</digression>
 
I am ok with sticking with wrapper objects at the moment. However it might be interesting to have a general mechanism to allow users to use their own converters when constructing objects from tables. I say "might" because I think it is mixed blessing: It would open the door to arbitrary instantiation of complex objects which goes against the general trend towards greater simplicity in code that cucumber and BDD promotes.
 

Good news! Supporting Java Beans turned out to be super easy. A single line of code actually:

Just annotate your bean class with @XStreamConverter(JavaBeanConverter.class) and enjoy the magic. I have just deployed a new snapshot in maven.

Aslak

Arnaud Bailly

unread,
Sep 16, 2011, 4:08:14 PM9/16/11
to cu...@googlegroups.com
On Fri, Sep 16, 2011 at 8:25 PM, aslak hellesoy <aslak.h...@gmail.com> wrote:


On Fri, Sep 16, 2011 at 6:05 PM, Arnaud Bailly <arnaud...@gmail.com> wrote:
Hi aslak,
Thanks a lot for taking the time to analyze this issue. Actually, I am definitely on line with your feelings about Javabeans and setters: They clearly suck and mutable state should be avoided if at all possible. Unfortunately, there exists quite a bunch of lines of code who do not feel that way and thinks Javabeans are cool and we should stick with this convention, and a lot of tools think the same way. I even myself indulge sometimes in this setter thing, for example when using spring as DI container.


<digression>
My favourite DI container is the "new" keyword ;-). Number 2 is PicoContainer (which I co-wrote 8 years ago, and still holds up nicely, powering IntelliJ IDEA's plugins and Cucumber-JVM).
Spring has become what it initially set out to replace because it was so complex (J2EE).
</digression>

Indeed !  I worked with pico some years ago and really liked it, much more than spring which was in its infancy at the time. Yes, "new" is great yet you often need more flexibility.

 
I am ok with sticking with wrapper objects at the moment. However it might be interesting to have a general mechanism to allow users to use their own converters when constructing objects from tables. I say "might" because I think it is mixed blessing: It would open the door to arbitrary instantiation of complex objects which goes against the general trend towards greater simplicity in code that cucumber and BDD promotes.
 

Good news! Supporting Java Beans turned out to be super easy. A single line of code actually:

Just annotate your bean class with @XStreamConverter(JavaBeanConverter.class) and enjoy the magic. I have just deployed a new snapshot in maven.


Wonderful!  will give it a try on monday at work.

thanks very much,
Arnaud

Philip Black-Knight

unread,
Jan 9, 2015, 11:13:48 AM1/9/15
to cu...@googlegroups.com, arnaud...@gmail.com
Sorry to resurrect an old post but I have essentially the same question regarding how to use a JavaBeanConverter (or other custom converter) with a DataTable. I'm trying to use data tables to populate domain objects and the reflections based converter causes 2 issues for me.
  1. I have to use the internal field names in the Gherkin files (mainly an issue because of the coding standards in place, forcing names like foobar_ to appear in the Gherkin files, not end of the world, but can make the steps harder to read)
  2. Objects are not created by use of the constructors, so defaults set up by the constructor are not handler. This causes a little bit of heartburn when later trying to persist these objects to the DB, and insert/update statements failing because of uninitialized data.
  3. Harder to embed complex objects in the DataTables
Let me start by saying I would love to be able to change how some of the coding standards, but at the moment that just isn't feasible, so I get change point number 1 at the moment.
The fact that Constructors are not called for the reflections based converter is where most of the pain come in because default values are not. Some of these could probably be avoided, but its also forcing me to figure out a whole lot more about related domain objects than I want to know about (or clutter the Gherkin files with). For example Boolean and Integer fields (big B/I) that are normally initialized in the constructor either need to be specified in the Gherkin files (which will quickly become unwieldily and pollute the feature files with unrelated noise). Many of these types of fields probably should be declared as boolean (little b), but that only helps halfway. If the constructor/initializer are setting these to something other than 0/false then I still need to bring that data into the Gherkin files, and a layer of duplication to the tests to fill in these default values as needed.

Annotating the domain classes with  @XStreamConverter(JavaBeanConverter.class) works, but the main thing I don't like about that is that now everything in my code that is using XStream has to use the same converter, or register their own for each class. Perhaps not the end of the world, but I am working with a fairly large legacy system, and I'm not 100% sure that adding the annotation won't break something else.  What I'd really like to do, is register converters when the Cucumber World is first initialized, that way I can set things up for each class within the tests without affecting the production code already in place.

It would also allow creating more complex associations using the data tables, e.g.:

Given I have the vendors:
| name  | tel          |
| Foo   | 123 456 7889 |
| Bar   | 321 654 7889 |
And I have the products:
| name  | vendor name | quantity |
| Foo 1 | Foo         | 12       |
| Foo 2 | Foo         | 22       |
| Bar 1 | Bar         | 22       |

There is obviously a bit of code that will need to get written by me to do this, but if the domain object is complex enough, it would pay to be able to use the JavaBean convertor (or delegate to it) for everything on the Products except the vendor. Vendor would need to be pull from the cucumber world, somehow, or perhaps just set after the initial object is created....
Its also possible I'm barking up the wrong tree regarding how to create the domain objects....suggestions welcome.

kpughc...@gmail.com

unread,
Feb 12, 2016, 4:49:40 PM2/12/16
to Cukes, arnaud...@gmail.com
To give a specific example of what I mean (to be sure we are on the same page) 

I've got a Customer class with a zero parameter constructor (either with attribute setters or within the zero parameter constructor):
 
public class CustomerData {
   private CustomerID  id = new CustomerID("1");
    private String name = "2";
    private String street = "3";
    private ZIPCode zipCode = new ZIPCode("99999") ;
    public Customer() { }

If the feature file has this:

Given Customer is:
| Id    | Name    |   Street     | Zip Code |
| 123   | John    | 1 Apple Lane | 27701    |

everything is initialized 

However if the feature file has this:

Given Customer is:
| Id    | Zip Code |
| 123   | 27705    |

then street and name are left null.  

With either of the methods I suggested, you can turn the null values into default values.  

Now the operation you are testing will be "do not care" what those attribute values are, but somewhere in the underlying implementation, there will probably be something that uses the attributes and would break if the values are null.

Ken


Reply all
Reply to author
Forward
0 new messages