Skip to content

Use TestContextManager in cucumber-spring #1470

Closed
@mpkorstanje

Description

@mpkorstanje

Summary

Support TestContextManager in cucumber-spring.

Expected Behavior

When using cucumber-spring @MockBean (and other annotations) should just work.

public class Example {

    @MockBean
    private User object;

    @Given("there is a User")
    public void there_is_a_User() {
        when(object.toString()).thenReturn("I've been mocked");
    }

    @Then("the User is mocked")
    public void the_user_is_mocked() throws Exception {
        assertThat(object.toString(), equalTo("I've been mocked"));
    }
}

Current Behavior

It is currently possible to use @Autowired e.g.:

public class Example {

    @Autowired
    private User object;

    @Then("the user is real")
    public void the_user_is_mocked() throws Exception {
        assertThat(object.toString(), equalTo("I am real"));
    }
}

But fields annotated with@MockBean are not mocked, the example fails with a null pointer exception.

Possible Solution

The causes of failure are two fold:

  1. There is no spring context declared (ContextConfiguration, BootstrapWith, ect) so the SpringFactory falls back to some minimal default. This can be resolved by adding @SpringBootTest to the step definition.

  2. The SpringFactory does not call

  • TestContextManager.beforeTestClass()
  • TestContextManager.prepareTestInstance().
  • TestContextManager.beforeTestMethod()
  • TestContextManager.beforeTestExecution()
  • TestContextManager.afterTestExecution()
  • TestContextManager.afterTestMethod()
  • TestContextManager.afterTestClass()

Instead step definitions are registered as beans in the Spring context. This had the advantage that step definitions can be auto wired into each other and constructor dependency injection just works. However it also puts the step definitions into the spring context which complicates things.

Given that step definitions calling each other is not a best practice I would not mind step definitions were no longer part of the spring context and instead treated as test instances (see SpringJUnit4ClassRunner.createTest). Any dependency injection would have to be done via @Autowired.

So to implement this properly the following would have to be done:

  • Require that all step definitions created have an empty constructor.
  • Map the life-cycle methods of the TestContextManager to that of the ObjectFactory.
  • Fail when there is no Spring context declared instead of creating a default.

Your Environment

  • Version used: cucumber-spring:4.0.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    🙏 help wantedHelp wanted - not prioritized by core team🧷 pinnedTells Stalebot not to close this issue⚡ enhancementRequest for new functionalitygood first issueGood for newcomers

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions