Java 将Cucumber场景数据表(HashMap)转换为方法调用

Java 将Cucumber场景数据表(HashMap)转换为方法调用,java,reflection,cucumber,cucumber-jvm,cucumber-junit,Java,Reflection,Cucumber,Cucumber Jvm,Cucumber Junit,我正在为一个应用程序创建一个验收测试框架,该应用程序使用Cucumber(-JVM)、Junit和java8接受XML文件(最多有数百个字段)。我创建了一个库,该库使用生成器接口将POJO序列化为XML,其中包含许多类(以避免有几十个构造函数,并具有各种声明性API),即: 我创建这个库是因为我想在测试中动态生成有效的XML文件。我想让我的场景读起来像英语一样,所以我决定使用数据表,而不是像这样写: Given I have a form with x field and with y fiel

我正在为一个应用程序创建一个验收测试框架,该应用程序使用Cucumber(-JVM)、Junit和java8接受XML文件(最多有数百个字段)。我创建了一个库,该库使用生成器接口将POJO序列化为XML,其中包含许多类(以避免有几十个构造函数,并具有各种声明性API),即:

我创建这个库是因为我想在测试中动态生成有效的XML文件。我想让我的场景读起来像英语一样,所以我决定使用数据表,而不是像这样写:

Given I have a form with x field
and with y field
and with z field
and ...
有50条不同的“和”线。所以它们看起来像这样:

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

我希望cucumber数据表中的键(转换为HashMap)映射到构建器模式的方法名。起初我认为使用lambdas和方法引用可以让我实现这一点,但我还没有找到一种方法

所以我的下一个想法是反思。我决定将Cucumber数据表键到方法名的映射存储在属性文件中,如:

//mapping.properties
X\ Field = setXField 
// the \ after X is to escape the space
但我遇到了一个问题:cucumber数据表中的一些字段映射到我的XML数据绑定库中的深度嵌套字段(由于XML模式)。例如:

“X字段”嵌套在字段中。因此,在我的测试方法中,我需要做:

AField aField = new AField(new XField());
但是对于java中的反射,您需要在了解事实之前了解参数数据类型(或者我认为是这样)。例如,如果我想找出与方法关联的参数类型:

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();

我相信我可以找到一个解决办法,但最终我似乎走上了一条“黑客”之路。有没有更好的方法来解决这个问题?或者我走的是“正确”的道路吗?

冒着招致版主愤怒的风险(明确的答案优先于链接),我将向您指出这一点。要获得列表到哈希表考虑[这个] < /P> 编辑

以下是参考链接中相关部分的摘录

将两列表转换为映射

Feature: Cucumber can convert a Gherkin table to a map.
  This an example of a simple price list.

  Scenario: A price list can be represented as price per item
    Given the price list for a coffee shop
      | coffee | 1 |
      | donut  | 2 |
    When I order 1 coffee and 1 donut
    Then should I pay 3
以及守则:

package se.thinkcode.steps;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

import java.util.Map;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class SimplePriceList {
    private Map<String, Integer> priceList;
    private int totalSum;

    @Given("^the price list for a coffee shop$")
    public void the_price_list_for_a_coffee_shop(Map<String, Integer> priceList) throws Throwable {
        this.priceList = priceList;
    }

    @When("^I order (\\d+) (.*) and (\\d+) (.*)$")
    public void i_order_coffee_and_donut(int numberOfFirstItems, String firstItem,
           int numberOfSecondItems, String secondItem) throws Throwable {

        int firstPrice = priceList.get(firstItem);
        int secondPrice = priceList.get(secondItem);

        totalSum += firstPrice * numberOfFirstItems;
        totalSum += secondPrice * numberOfSecondItems;
    }

    @Then("^should I pay (\\d+)$")
    public void should_I_pay(int expectedCost) throws Throwable {
        assertThat(totalSum, is(expectedCost));
    }

}
包se.thinkcode.steps;
导入cucumber.api.java.en.Given;
导入cumber.api.java.en.Then;
导入cucumber.api.java.en.When;
导入java.util.Map;
导入静态org.hamcrest.CoreMatchers.is;
导入静态org.junit.Assert.assertThat;
公共类SimplePriceList{
私人地图价格表;
私人整数总和;
@给定(“^咖啡店价格表$”)
公共作废咖啡店的价格表(地图价格表)可丢弃{
this.priceList=priceList;
}
@当(“^I订单(\\d+(*)和(\\d+(*)$”)
公共无效i_订单_咖啡_和_甜甜圈(int numberOfFirstItems,String firstItem,
int numberOfSecondItems,字符串secondItem)抛出可丢弃{
int firstPrice=priceList.get(firstItem);
int secondPrice=priceList.get(secondItem);
totalSum+=firstPrice*numberOfFirstItems;
合计+=第二价格*第二项目数量;
}
@然后(“^I应该支付(\\d+$”)
公共无效应支付(int预期成本)可丢弃{
资产(总额,即(预期成本));
}
}

您谈论了很多关于您的实现的事情。你能分享更多关于你的问题吗?我感觉你在用黄瓜,但它不擅长。但一个具体的例子可能会更好地了解您的目标。@ThomasSundberg所说的问题您是指我正在测试的应用程序吗?本质上,我正在测试一个接受XML文件的web服务。我构建了一个动态生成xml文件的工具。其中一些XML文件有125个以上的字段,我需要测试业务规则,比如说25个不同的字段。因此,我需要在cucumber中表示每个XML文件中需要的数据。我认为数据表将是最明智的解决方案,而不是a)在黄瓜场景中使用100行b)用一种简练的方式来表示100个字段的值。@ThomasSundberg另外,我无法更改应用程序以将XML文件分解为更小的子XML文件。我同意MikeJRamsey56。BeanUtils是一种将地图转换为数据结构的“更好”方法。我同意你的观点——按照SO标准,这显然是一个糟糕的答案。这是一个“仅链接”的答案,你应该将目的页面的要点包含在你的答案中,否则它可能会被关闭。@AleksG我喊“叔叔!”。谢谢你提醒我改进答案。当时我太忙了,只想给OP一个线索。当时可能应该是一个评论。非常清晰的回答。。与问题本身不同:)
package se.thinkcode.steps;

import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;

import java.util.Map;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

public class SimplePriceList {
    private Map<String, Integer> priceList;
    private int totalSum;

    @Given("^the price list for a coffee shop$")
    public void the_price_list_for_a_coffee_shop(Map<String, Integer> priceList) throws Throwable {
        this.priceList = priceList;
    }

    @When("^I order (\\d+) (.*) and (\\d+) (.*)$")
    public void i_order_coffee_and_donut(int numberOfFirstItems, String firstItem,
           int numberOfSecondItems, String secondItem) throws Throwable {

        int firstPrice = priceList.get(firstItem);
        int secondPrice = priceList.get(secondItem);

        totalSum += firstPrice * numberOfFirstItems;
        totalSum += secondPrice * numberOfSecondItems;
    }

    @Then("^should I pay (\\d+)$")
    public void should_I_pay(int expectedCost) throws Throwable {
        assertThat(totalSum, is(expectedCost));
    }

}