Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow parameter types acces to the test context #1458

Closed
mpkorstanje opened this issue Sep 12, 2018 · 5 comments
Closed

Allow parameter types acces to the test context #1458

mpkorstanje opened this issue Sep 12, 2018 · 5 comments
Labels
🙏 help wanted Help wanted - not prioritized by core team 🧷 pinned Tells Stalebot not to close this issue ⚡ enhancement Request for new functionality
Milestone

Comments

@mpkorstanje
Copy link
Contributor

mpkorstanje commented Sep 12, 2018

Summary

Allow parameter types to be defined as part of the Glue.

Expected Behavior

The transform of a parameter type should be able to access the test context. This allows parameter types to be mapped to objects which can only be created by services inside the test context.

For example:

    Given the awesome catalog
    When a user places the awestruck eels in his basket
    Then you will be shocked at what happened next

And I would like to implement the second step as When("a user places the {product name} in his basket", (Product p) -> ...}

Then my parameter type transform would have to know about "awestruck eels" as a product. However this product doesn't exist until after the awesome catalog has been loaded. So the only way to create an instance of this product would be by converting it in the step itself. This is quite distracting.

If I could instead define the parameter type as part of the glue it would be possible to do:

public SomeGlueClass(Catalog catalog) implements En {
  ParameterType("product", ".*", Product.class, (String name) -> catalog.findProductByName(name));
}
private final Catalog catalog;

@ParameterType(name="product", pattern=".*")
public Product findProductByName(String name) {
  return catalog.findProductByName(name);
}

Possible Solution

Create annotations and lambda's to register a parameter type and pick them up as part of the glue. This will require a hierarchical type registry that delegates to the parent type registry when a parameter type could not be found.

@mpkorstanje mpkorstanje added ⚡ enhancement Request for new functionality Cucumber Expressions labels Sep 12, 2018
@mpkorstanje mpkorstanje changed the title Define parameter types as part of the glue Allow parameter types acces to the test context Sep 12, 2018
@mpkorstanje mpkorstanje added the 🙏 help wanted Help wanted - not prioritized by core team label Sep 12, 2018
@aslakhellesoy
Copy link
Contributor

Makes sense. We already did something similar in cucumber/cucumber-js#948 and cucumber/cucumber-ruby#1213

@stale
Copy link

stale bot commented Nov 12, 2018

This issue has been automatically marked as stale because it has not had recent activity. It will be closed in a week if no further activity occurs.

@stale stale bot added the ⌛ stale Will soon be closed by stalebot unless there is activity label Nov 12, 2018
@aslakhellesoy aslakhellesoy removed the ⌛ stale Will soon be closed by stalebot unless there is activity label Nov 12, 2018
@mpkorstanje mpkorstanje added the 🧷 pinned Tells Stalebot not to close this issue label Nov 13, 2018
@mpkorstanje
Copy link
Contributor Author

mpkorstanje commented Jun 30, 2019

If any one is interested in picking this up there is a small prototype in #1677.

@mpkorstanje mpkorstanje added this to the 5.0.0 milestone Jul 3, 2019
mpkorstanje added a commit that referenced this issue Jul 28, 2019
# Summary

By annotating methods parameter and data table types can be defined as 
part of the Glue. This enables them to access the test context and makes
them eligible for dependency injection. Additionally the type registry is
now created for each feature. This means the language of the feature will
be used to convert numbers.

## Details

## Parameter and DataTable Type

Introduces the `@ParameterType` and `@DataTableType` annotations. This
allows parameter and datatable types to be mapped to objects which can
only be created by services inside the test context.

For example in this scenario
```gherkin
Given the awesome catalog
When a user places the awestruck eels in his basket
Then you will be shocked at what happened next
```

We are now able to look up the "awestruck eels" in the "awesome" catalog.

```java
private final Catalog catalog;

@ParameterType(".*")
public Product product(String name) {
  return catalog.findProductByName(name);
}
```
## Default Transformer

It is now also possible to register default transformers using
annotations. Default transformers allow you to specific a transformer that
will be used when there is no transform defined. This can be combined with
an object mapper like Jackson to quickly transform well known string
representations to Java objects.

 * `@DefaultParameterTransformer`
 * `@DefaultDataTableEntryTransformer`
 * `@DefaultDataTableCellTransformer`

 ```java
package com.example.app;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.cucumber.java.DefaultDataTableCellTransformer;
import io.cucumber.java.DefaultDataTableEntryTransformer;
import io.cucumber.java.DefaultParameterTransformer;

import java.lang.reflect.Type;

public class DataTableSteps {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @DefaultParameterTransformer
    @DefaultDataTableEntryTransformer
    @DefaultDataTableCellTransformer
    public Object defaultTransformer(Object fromValue, Type toValueType) {
        return objectMapper.convertValue(fromValue, objectMapper.constructType(toValueType));
    }
}
```

## Localization

Some languages uses comma's rather then points to separate decimals.
Previously to parse these properly you'd have to use
`TypeRegistryConfigurer.locale` to set this globally. When not explicitly
provided Cucumber will now take the language from the feature file. This
makes the following work without additional configuration:

```gherkin
# language: fr
Fonctionnalité: Concombres fractionnaires

  Scénario: dans la ventre
    Étant donné j'ai 5,5 concombres fractionnaires
```
```java
@Étantdonné("j'ai {bigdecimal} concombres fractionnaires")
public void jAiConcombresFractionnaires(BigDecimal arg0) {
    assertThat(arg0, is(new BigDecimal("5.5")));
}
```

# Motivation & Context

Fixes #851.
Fixes #1458.
@fslev
Copy link

fslev commented Aug 13, 2019

Is there also support for anonymous parameter types {} ?

@mpkorstanje
Copy link
Contributor Author

Yes. They go through the default parameter transformer which you can now define like a step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🙏 help wanted Help wanted - not prioritized by core team 🧷 pinned Tells Stalebot not to close this issue ⚡ enhancement Request for new functionality
Projects
None yet
Development

No branches or pull requests

3 participants