lundi 29 février 2016

Converting a Cucumber scenario DataTable (HashMap) to method calls

I am creating an acceptance test framework for an application that accepts XML files (with up to hundreds of fields) using Cucumber(-JVM), Junit, Java 8. I have created a library that serializes POJOs to XML with a lot of classes using the Builder interfaces (to avoid having dozens of constructors and to have a declarative API of sorts), i.e.:

new Form.Builder
    .setThisField(withThis)
    .setThatField(withThat)
    .build();

I want my cucumber scenarios to read like English, so I decided to use data tables instead of writing something like:

Given I have a form with x field
and with y field
and with z field
and ...

With 50 different 'and' lines. So they look like this:

Given that I have Form B101 with the following fields:
  | X Field        | X Value         |
  | Y Field        | Y Value         |
  | Z Field        | Z Value         |

The Problem:

I want the keys in the cucumber data table (which is turned into a HashMap) to map to method names for my builder pattern. At first I thought using lambdas and method references may let me accomplish this, but I have yet to find a way.

So my next thought was reflection. I decided to store a mapping from a Cucumber Data table key to a method name in a properties file like:

//mapping.properties
X\ Field = setXField 
// the \ after X is to escape the space

I have run into a problem though: some of the fields in the cucumber data table map to deeply nested fields (due to the XML schema) in my XML Data binding library. So for example:

"X Field" is nested within A Field. So in my test method I need to do:

AField aField = new AField(new XField());

But with reflection in java, you need to know the parameter data types before the fact (or so I think). For example, if I want to find out what parameter types are associated with a method:

Class[] paramString = new Class[1];
paramString[0] = AField.class;
// So I need to know before the fact that methodName (below) has a parameter
// of type AField in order to .... get the parameter types of methodName.


// this is legal because a method with methodName exists and it takes
// one parameter with type AField.
Class[] parameterTypes = 
  formB.getClass().getMethod(methodName, paramString).getParameterTypes();

// this is not legal. no method named methodName which doesn't 
// take parameters exists
Class[] parameterTypes = 
  formB.getClass().getMethod(methodName).getParameterTypes();

I'm sure I could find a workaround for this, but ultimately it seems like I'm going down a 'hacky' path. Is there a better way to approach this problem? Or am I on the 'correct' path?





Aucun commentaire:

Enregistrer un commentaire