serenity-bdd / serenity-cucumber

Cucumber integration for the Serenity BDD Reporting library
Other
78 stars 74 forks source link

Failed to instantiate - this class doesn't have an empty or a page enabled constructor #102

Closed nbarrett closed 6 years ago

nbarrett commented 6 years ago

Whilst attempting to upgrade my serenity project to Serenity 1.7.3, Cucumber 2.1, Serenity Cucumber 1.6.4 in order to try and resolve serenity-core issue #1020. I now get this exception during the initialisation:

cucumber.runtime.CucumberException: Failed to instantiate class <ClassName> - this class doesn't have an empty or a page enabled constructor"

    at cucumber.runtime.SerenityObjectFactory.newInstance(SerenityObjectFactory.java:66)
    at cucumber.runtime.SerenityObjectFactory.cacheNewInstance(SerenityObjectFactory.java:51)
    at cucumber.runtime.SerenityObjectFactory.getInstance(SerenityObjectFactory.java:41)
    at cucumber.runtime.java.JavaHookDefinition.execute(JavaHookDefinition.java:60)
    at cucumber.runtime.HookDefinitionMatch.runStep(HookDefinitionMatch.java:17)
    at cucumber.runner.UnskipableStep.executeStep(UnskipableStep.java:22)
    at cucumber.api.TestStep.run(TestStep.java:83)
    at cucumber.api.TestCase.run(TestCase.java:58)
    at cucumber.runner.Runner.runPickle(Runner.java:80)
    at cucumber.runtime.junit.PickleRunners$NoStepDescriptions.run(PickleRunners.java:140)
    at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:68)
    at cucumber.runtime.junit.FeatureRunner.runChild(FeatureRunner.java:23)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at cucumber.runtime.junit.FeatureRunner.run(FeatureRunner.java:73)
    at cucumber.api.junit.Cucumber.runChild(Cucumber.java:99)
    at cucumber.api.junit.Cucumber.runChild(Cucumber.java:41)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at cucumber.api.junit.Cucumber$1.evaluate(Cucumber.java:108)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NoSuchMethodException: <ClassName>.<init>()
    at java.lang.Class.getConstructor0(Class.java:3082)
    at java.lang.Class.getConstructor(Class.java:1825)
    at cucumber.runtime.SerenityObjectFactory.newInstance(SerenityObjectFactory.java:62)
    ... 34 more

I also experimented with different cucumber Object factories and ended up changing my cucumber.properties files from this cucumber.api.java.ObjectFactory=cucumber.runtime.java.guice.impl.GuiceFactory to this: cucumber.api.java.ObjectFactory=cucumber.runtime.SerenityObjectFactory

wakaleo commented 6 years ago

Does the class have an empty constructor?

nbarrett commented 6 years ago

No, sorry still pasting info 😄 - the class has always used guice for injection so:

@ScenarioScoped
public class LADSSteps {

    private final ScenarioContext context;
    private final ActiveMQService messageService;
    private Scenario scenario;
    @Inject
    public LADSSteps(ScenarioContext context, ActiveMQService messageService) {
        this.context = context;
        this.messageService = messageService;
    }

    @Before
    public void beforeExecution(Scenario scenario) {
        this.scenario = scenario;
    }

(etc)
wakaleo commented 6 years ago

Cucumber can only have one dependency injection mechanism, and Serenity is one, so you can't have both Guice and Serenity to instantiate Cucumber objects.

nbarrett commented 6 years ago

So has this changed as a result of a cucumber or a Serenity change? These steps have been working fine for years with cucumber 1.x and also more recently 'under' Serenity 1.5.7 which our project has now cut over to.

I can't find any relevant documentation in the cucumber.io site that talks about how to migrate 1.x code to 2.x. Seems like a real minefield!

wakaleo commented 6 years ago

Most likely a Cucumber change, as the Serenity injection logic (in the SerenityObjectFactory) hasn't changed since cucumber 1.x. It's a massive rewrite of the Cucumber API; I'm not sure of the impact on things like that. You may have to sniff around the code to see what you can find.

nbarrett commented 6 years ago

Okay, thanks... I guess this is why I want to be able to leave Cucumber at 1.x but still get updates to Serenity. I have several hundred steps files in my client's codebase. with lots of add-ones like groovy which doesn't seem to be cucumber 2.x ready anyway, so we are probably not even able to move. I suspect other serenity customers may also suffer from this inertia.... In the meantime I've joined https://cucumberbdd.slack.com/ and will try and understand how to migrate to 2.x with Aslak and co 😞

wakaleo commented 6 years ago

Have you tried upgrading serenity-core without upgrading serenity-cucumber?

nbarrett commented 6 years ago

no... is that possible?! That's all I really want to do...I just got the impression that it had to be an all-or-nothing upgrade.

wakaleo commented 6 years ago

Upgrading cucumber without serenity would be impossible. Upgrading serenity without cucumber might work, I don't think there are any breaking changes in the Serenity API. You would have to play round with it to see though.

RStrizhak commented 6 years ago

Hi guys, I have the same issue, I use the following versions:

        <java.version>1.8</java.version>
        <serenity-core.version>1.8.1</serenity-core.version>
        <serenity-cucumber.version>1.6.5</serenity-cucumber.version>
        <cucumber-java8.version>1.2.5</cucumber-java8.version>

My Step definition class:

import cucumber.api.java8.En; import core.logging.Logger;

public class MainPageDefinitions implements En {

public MainPageDefinitions() {
    Given("User opens main page", () -> {
        Logger.out.debug("open main page");
    });

    Given("User clicks 'Login' button on main page", () -> {
        Logger.out.debug("click login button");
    });
}

}

I tried to downgrade serenity-cucumber to 1.6.4-2 - nothing changed. Below 1.6.2 version I got another exceptions... Do you have any updates?

RStrizhak commented 6 years ago

my bad, problems with the obsolete cucumber-java8 library

UtsaChakraborty commented 6 years ago

If the Serenity and Cucumber versions are up to date and still this error is faced, : "this class doesn't have an empty or a page enabled constructor".... does it mean a empty constructor is mandatory for stepdefinition classes?

wakaleo commented 6 years ago

Yes

taelus commented 6 years ago

I'm confused by this because I have the project running for one product and I instantiate a utilities class in the constructor of the steps definitions class, but a coworker recently took a much reduced version and tried to run and now they're hitting this issue. We have the exact same versions of core and cucumber.

wakaleo commented 6 years ago

Any @Steps-annotated class needs an empty constructor. Otherwise Serenity won't know how to instantiate it.

taelus commented 6 years ago

Are we talking about the @Given-When-Then class or the @Steps annotated class?

wakaleo commented 6 years ago

Step definition classes also need to be instantiated by Serenity.

taelus commented 6 years ago

Sorry for the poorly worded question. What I mean is that in the structure there are step definitions and then test steps (the ones referenced under the @Steps when the object is declared in the step definition class). Do both need empty constructors for Serenity in instantiate them?

wakaleo commented 6 years ago

Yes

taelus commented 6 years ago

Ok, so I'll write off it working in my main project as luck and remove the pattern from future versions.

onlybiju commented 4 years ago

I am facing the same problem, I did instantiate the my class but still getting the exception "Failed to instantiate class com.stepdefinition.TestAutomationsteps - this class doesn't have an empty or a page enabled constructor." The same if i run it in my local machine it works fine. But when I run the same in Jenkins it gives me the exception. Here are all my Serenity version info testCompile 'net.serenity-bdd:serenity-core:2.0.90' testCompile 'net.serenity-bdd:serenity-junit:2.0.90' testCompile 'net.serenity-bdd:serenity-cucumber:1.9.49' testCompile 'net.serenity-bdd:serenity-screenplay:2.0.90' testCompile 'net.serenity-bdd:serenity-rest-assured:2.0.90' testCompile 'net.serenity-bdd:serenity-reports:2.0.90' Can someone please help?

wakaleo commented 4 years ago

The Jenkins thing is weird, but as the message says: "this class doesn't have an empty or a page enabled constructor." - make sure you have a constructor with a WebDriver parameter.