JGiven icon indicating copy to clipboard operation
JGiven copied to clipboard

how to validate ScenarioState in order to generate indicative exception message and/or stack trace

Open adrian-herscu opened this issue 4 years ago • 4 comments

Consider having a Given stage with a ProvidedState field:

class Given extends Stage<Given> {
        @ScenarioState
        private final ThreadLocal<WebDriver> webDriver = new ThreadLocal<>();
        Given init(final WebDriver webDriver) {
            this.webDriver.set(webDriver);
            return self();
        }
}

Now in a scenario:

public void shouldDoSomething() {
    // forgotten to call: given().init(someWebDriver);
    when()...
    then()...
}

Running the above will generate a NullPointerException with a stack trace like:

java.lang.NullPointerException
	at com.tngtech.jgiven.impl.ScenarioModelBuilder.setStatus(ScenarioModelBuilder.java:244)
	at com.tngtech.jgiven.impl.ScenarioModelBuilder.scenarioFailed(ScenarioModelBuilder.java:316)
	at com.tngtech.jgiven.impl.ScenarioExecutor.failed(ScenarioExecutor.java:470)
	at com.tngtech.jgiven.testng.ScenarioTestListener.onTestFailure(ScenarioTestListener.java:94)
	at org.testng.internal.TestListenerHelper.runTestListeners(TestListenerHelper.java:67)
	at org.testng.internal.Invoker.runTestListeners(Invoker.java:1389)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:636)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:719)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:989)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:125)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109)
	at org.testng.TestRunner.privateRun(TestRunner.java:648)
	at org.testng.TestRunner.run(TestRunner.java:505)
	at org.testng.SuiteRunner.runTest(SuiteRunner.java:455)
	at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:450)
	at org.testng.SuiteRunner.privateRun(SuiteRunner.java:415)
	at org.testng.SuiteRunner.run(SuiteRunner.java:364)
	at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
	at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:84)
	at org.testng.TestNG.runSuitesSequentially(TestNG.java:1208)
	at org.testng.TestNG.runSuitesLocally(TestNG.java:1137)
	at org.testng.TestNG.runSuites(TestNG.java:1049)
	at org.testng.TestNG.run(TestNG.java:1017)
	at com.intellij.rt.testng.IDEARemoteTestNG.run(IDEARemoteTestNG.java:66)
	at com.intellij.rt.testng.RemoteTestNGStarter.main(RemoteTestNGStarter.java:110)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:64)

which does not indicate what the real problem is.

Is there some way to validate ScenarioState's and generate some custom message?

One way would be to add to each step method the following requiresNonNull(webDriver, "you forgot to call given")...

Is there someway to make it happen during the stage initialization?

adrian-herscu avatar Jun 23 '20 06:06 adrian-herscu

Hi @adrian-herscu, while I like the idea of having descriptive error messages, this seems to be something that requires a rather large amount of work. I'd need to take a deep look into the code first to figure out what would need to be done here.

l-1squared avatar Jun 23 '20 14:06 l-1squared

Hi @adrian-herscu, while I like the idea of having descriptive error messages, this seems to be something that requires a rather large amount of work. I'd need to take a deep look into the code first to figure out what would need to be done here.

How the injection happens? Which injection mechanism is used? May there is some feature in the dependency injection framework to help with this?

adrian-herscu avatar Jun 23 '20 15:06 adrian-herscu

Hi, I tried to understand and answer your question. The injection of fields should happen in ValueInjector. There are additional injection places for @ScenarioState. Hence the injection is mostly self-written with a little help of ByteBuddy to help with proxying stages.

I need to mention however that I failed miserably at the reproduction of your example. Which is very unfortunate, because I wanted to use that to get a grasp of where one might actually could hook in to prevent the error you described.

Maybe @janschaefer can provide a little more insight, on where such a hook would need to be placed.

l-1squared avatar Jun 25 '20 15:06 l-1squared

I need to mention however that I failed miserably at the reproduction of your example. Which is very unfortunate, because I wanted to use that to get a grasp of where one might actually could hook in to prevent the error you described.

Can I help with that?

adrian-herscu avatar Dec 11 '23 01:12 adrian-herscu