fitnesse icon indicating copy to clipboard operation
fitnesse copied to clipboard

Support conditional execution of script/scenario tables

Open roboaks opened this issue 5 years ago • 25 comments

We have worked with FitNesse extensively over the past 5 years and use it as the foundation of our AFT platform. The FitNesse dev team has added a couple features we’ve suggested in the past (e.g. creating FitNesse variables from name/value pairs passed via the API), for which we are very appreciative.

We use script and particularly scenario tables extensively, and there are many situations where we would like to make execution of a script/scenario table contingent on a simple, boolean condition (e.g. the return from a fixture method, the value in a symbol, etc.). This would constitute an enormous improvement for us—and perhaps for others.

I can think of a couple approaches:

  1. Add an exit if keyword to script/scenario table processing. If this phrase is in the first cell of a row, then a symbol, variable, or function call should follows it and should be resolvable to a boolean. If true, scenario/script table processing will terminate immediately.
  2. Add two new table types, ConditionalScript/ConditionalScenario (though I can imagine all sorts of complications with this approach)

I know that conditional processing may not, in general, be something that the FitNesse community embraces, but I can tell you that, as a team that works with FitNesse every day in many different contexts, there are times when allowing a test to perform differently based on runtime conditions would make life so much easier. We have come up with some workarounds over the years, but the approach described above would be much better.

Thanks for considering!

roboaks avatar Dec 26 '19 15:12 roboaks

Hi Rob,

Have a look at https://github.com/praegus/toolchain-fitnesse-plugin

It contains a conditional script table that expects a Boolean (js) condition in the first row and executes only if that condition evaluates to true. Scenario's can be called from the conditional script table. Handle with care ;-)

tcnh avatar Dec 26 '19 15:12 tcnh

Hi Tom:

This is really close to what we need!

There are two issues:

  1. We need a conditional scenario table. There's no way we can arrange things so that calling a scenario table from a conditional script would work. That's because (among other things) our scenarios are deeply nested so we often need to impose the condition several levels down.
  2. It would be helpful, though not absolutely required, if the expression could be a fixture call as well as a symbol or variable. Otherwise, we would need to always arrange to set a symbol prior to the script (or pass a parameter, if you created a conditional scenario table).

#1 is the showstopper though. Without a conditional scenario table, alas, your solution is not usable for us. Is there any chance you would be able to help us with that? I think we could arrange a paid engagement if you are willing.

Thanks!

roboaks avatar Dec 26 '19 16:12 roboaks

@roboaks have you looked at creating your own FitNesse plugin providing a custom scenario subclass to slim to produce the conditional behaviour you are looking for?

Slim's table types are intended to be extendable and making a subclass of ScenarioTable that has the functionality you are looking for does not seem that complex, at first glance. Although I'm not familiar with 'conditional script table', I'm assuming it is a custom subclass of ScriptTable that does something similar.

An example of a custom scenario table subclass can also be found in my own plugin where I define table template, a variation on a normal scenario

fhoeben avatar Dec 26 '19 16:12 fhoeben

@fhoeben: That's certainly a possibility. We have deep Java expertise and, while we don't know the FitNesse code base, if it requires a relatively simple subclass of ScenarioTable, we would definitely consider that approach. @tcnh: is that the approach you took (subclassing ScriptTable)?

roboaks avatar Dec 26 '19 16:12 roboaks

Yes, that's the same approach. I'll have a look if can easily add a conditional scenario table

tcnh avatar Jan 02 '20 14:01 tcnh

Thanks so much Tom. That would be so helpful for us.

roboaks avatar Jan 02 '20 14:01 roboaks

Hi Rob, have a look at the latest version of our plugin. It contains a conditional scenario table, although it has a few limitations. (the testcontext is not being updated between executions if run as a decision table; the expression in row 1 is javascript and knows all slim symbols as variables, so if you need to use the outcome of a fixture ethod, you have to pass it as a slim symbol). May still be too limitied for your use case, but could possibly serve as inspiraton for a version that allows for more sophisticated conditional behaviour.

Happy testing :)

tcnh avatar Jan 30 '20 08:01 tcnh

Much appreciated Tom!

To be clear on the limitations: are you saying that if a symbol S is set to abc in script table A and A calls Conditional Scenario table B that has | $S == 'abc' | in row 1, then the scenario will not be executed?

Are you saying that, on the other hand, if a symbol S is set to abc in script table A and A calls scenario table B, which calls Conditional Scenario table C that has | $S == 'abc' | in row 1, then the scenario will be executed?

roboaks avatar Jan 30 '20 12:01 roboaks

Hi rob,

yes, that's correct. See below for a copy/paste of the documentation wiki page I'm still working on. Should clarify the limitations :)

!note Use with caution!

A conditional scenario is only executed if the (Javascript) expression in the first row/cell evaluates to true.
The expression can reference slim symbols by name, as if they were variables.

!2 Example

Set a value only if a slim symbol has a specific value

|conditional scenario|set value|value|if x is|val|
|x == '@{val}'                                   |
|show                |value of |$x               |
|$x=                 |value of |@{value}         |
|check               |value of |$x   |@{value}   |

|script|string fixture|
|$x=   |value of |abc |

|script                   |
|set value|123|if x is|abc|

|script                   |
|set value|456|if x is|123|

!2 Limitations

Conditional scenario's can be used in the same script table, however in that case the expression will only know the slim context as it exists when the table is called.
The expression context cannot be updated within the same table.

This means that setting a symbol in a script or conditional scenario has to happen in a table '''before''' the table that calls the conditional scenario table.

!4 An example

!5 In the following table, the conditional scenario will not be executed, because $x is set to foo in the same table as the conditional scenario call

|script                        |
|$x=      |value of|foo        |
|set value|bar     |if x is|foo|

!5 In order to make this script work, one has to split the assignment and expression over two tables:

Table 1: ''Assign''

|script              |
|$x=|value of|someVal|

Table 2: ''Expression''

|script                       |
|set value|bar|if x is|someVal|


tcnh avatar Jan 30 '20 15:01 tcnh

@tcnh, we also plan to use fitnesse as our test framework. And currently we are looking into the conditional script/scenario.

Actually I cherry picked the code of https://github.com/praegus/toolchain-fitnesse-plugin & compile to our release jar.

But during that, I met this problem. It says "ScriptEngine engine" is null. However, I see it was initialized by SlimExpressionEvaluator("JavaScript"). So can you help me to fix it? And I do not know how to get the ScriptEngine, maybe I missed something.

zhongwei1981 avatar Apr 28 '20 03:04 zhongwei1981

The SliMExpressionEvaluator uses Nashorn, which is deprecated, bus afaik still present until Java 14. What version of Java are you using to run?

tcnh avatar Apr 28 '20 08:04 tcnh

@tcnh , JDK 1.8.202

In ConditionalScriptTable.java - invokeAction(), I added these code and found:

ScriptEngineManager engineManager = new ScriptEngineManager(); ScriptEngine jsEngine = engineManager.getEngineByName("JavaScript"); log.info("#### 3.0.1 jsEngine: " + jsEngine); // Error: jsEngine is "null"

zhongwei1981 avatar Apr 28 '20 09:04 zhongwei1981

@tcnh, I do not know how to include the plugin of toolchain-fitnesse-plugin. I did download the code of it by git, not found jar. So I compiled it by "mvn clean package". Then copy all the required jar files to lib to start fitnesse. And got these log of below, which looks conditional script/scenario is not registered.

fitnesse-20200404 & toolchain-fitnesse-plugin 2.0.3 & Plugins = nl.praegus.fitnesse.PraegusPluginFeatureFactory

java -Djava.ext.dirs=lib -jar lib/fitnesse-20200404.jar -p 2222 -e 0 -l ./logs [Toolchain Plugin] Registering AutoCompleteResponder (?autoComplete). [Toolchain Plugin] Registering TocResponder (?tableOfContents). [Toolchain Plugin] Registering UpdateTagsResponder (?updateTags). [Toolchain Plugin] Registering MavenProjectVersionsResponder (?mavenVersions). [Toolchain Plugin] Registering TestRecentHistoryResponder (?recentTestHistory). [Toolchain Plugin] Registering AllTagsResponder (?allTags). Registered responders from: nl.praegus.fitnesse.PraegusPluginFeatureFactory Registered wiki page factories from: nl.praegus.fitnesse.PraegusPluginFeatureFactory Registered test system factories from: nl.praegus.fitnesse.PraegusPluginFeatureFactory [Toolchain Plugin] Registering table specific css decorator classes. Registered Symbol types from: nl.praegus.fitnesse.PraegusPluginFeatureFactory Loaded custom comparator inverse: fitnesse.slim.test.InverseComparator Bootstrapping FitNesse, the fully integrated standalone wiki and acceptance testing framework. root page: fitnesse.wiki.fs.FileSystemPage at ./FitNesseRoot#latest logger: /opt/home/src/toolchain-fitnesse-plugin/./logs authenticator: fitnesse.authentication.PromiscuousAuthenticator page factory: fitnesse.html.template.PageFactory page theme: bootstrap Starting FitNesse on port: 2222

zhongwei1981 avatar Apr 28 '20 09:04 zhongwei1981

That's strange. The PluginFeatureFactory registers responders, tables and sybols, but in your case, it seems like

 @Override
    public void registerSlimTables(SlimTableFactory slimTableFactory) throws PluginException {
        super.registerSlimTables(slimTableFactory);
        LOG.info("[Toolchain Plugin] Registering debug script table. Be sure to add 'pause test fixture' from nl.praegus:toolchain-fixtures as a library");
        add(slimTableFactory, "debug script", PausingTable.class);
        LOG.info("[Toolchain Plugin] Registering conditional script table.");
        add(slimTableFactory, "conditional script", ConditionalScriptTable.class);
        LOG.info("[Toolchain Plugin] Registering conditional scenario table.");
        add(slimTableFactory, "conditional scenario", ConditionalScenarioTable.class);
        LOG.info("[Toolchain Plugin] Registering Looping scenario table.");
        add(slimTableFactory, "looping scenario", LoopingScenarioTable.class);

    }

is not being executed. Are you overriding that method in your own code somewhere without calling its super maybe?

Easiest way to use the plugin is to just get the jar with all dependencies from maven: https://mvnrepository.com/artifact/nl.praegus/toolchain-fitnesse-plugin/2.0.3

download the jar-with-dependencies and place it in your plugins directory. It should automatically register.

tcnh avatar Apr 28 '20 09:04 tcnh

Not works still. However, used apache.commons.jexl3 to calculate Expression than javax.script.ScriptEngine, and it's OK for me.

zhongwei1981 avatar May 08 '20 09:05 zhongwei1981

hi how to add the plugins after downloading jars

neeraj5d5 avatar Dec 09 '20 06:12 neeraj5d5

Hi @neeraj5d5 see 2 comments above yours:

download the jar-with-dependencies and place it in your plugins directory. It should automatically register.

Please be sure to use the latest version: https://repo.maven.apache.org/maven2/nl/praegus/toolchain-fitnesse-plugin/2.0.10/

tcnh avatar Dec 09 '20 09:12 tcnh

Hi @neeraj5d5 see 2 comments above yours:

download the jar-with-dependencies and place it in your plugins directory. It should automatically register.

Please be sure to use the latest version: https://repo.maven.apache.org/maven2/nl/praegus/toolchain-fitnesse-plugin/2.0.10/

sorry but in fitness in how to add it is it similar or different I am getting following error

Exception in thread "main" java.lang.NoClassDefFoundError: fitnesse/wikitext/parser/decorator/ParsedSymbolDecorator at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) at java.net.URLClassLoader.defineClass(URLClassLoader.java:468) at java.net.URLClassLoader.access$100(URLClassLoader.java:74) at java.net.URLClassLoader$1.run(URLClassLoader.java:369) at java.net.URLClassLoader$1.run(URLClassLoader.java:363) at java.security.AccessController.doPrivileged(Native Method) at java.net.URLClassLoader.findClass(URLClassLoader.java:362) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) at nl.praegus.fitnesse.PraegusPluginFeatureFactory.registerSymbolTypes(PraegusPluginFeatureFactory.java:50) at fitnesse.plugins.PluginsLoader.loadSymbolTypes(PluginsLoader.java:83) at fitnesse.ContextConfigurator.makeFitNesseContext(ContextConfigurator.java:151) at fitnesseMain.FitNesseMain.launchFitNesse(FitNesseMain.java:66) at fitnesseMain.FitNesseMain.launchFitNesse(FitNesseMain.java:55) at fitnesseMain.FitNesseMain.main(FitNesseMain.java:35) Caused by: java.lang.ClassNotFoundException: fitnesse.wikitext.parser.decorator.ParsedSymbolDecorator at java.net.URLClassLoader.findClass(URLClassLoader.java:382) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 18 more

neeraj5d5 avatar Dec 09 '20 10:12 neeraj5d5

I think your FitNesse version is outdated. Are you using 20200501?

tcnh avatar Dec 09 '20 10:12 tcnh

Hi @neeraj5d5 see 2 comments above yours:

download the jar-with-dependencies and place it in your plugins directory. It should automatically register.

Please be sure to use the latest version: https://repo.maven.apache.org/maven2/nl/praegus/toolchain-fitnesse-plugin/2.0.10/

sorry but in fitness in how to add it is it similar or different

I think your FitNesse version is outdated. Are you using 20200501?

I am using fitness 20160515 stand-alone jar in fitness for appiane

neeraj5d5 avatar Dec 09 '20 10:12 neeraj5d5

That will not work. The plugin is built with the latest release version, as we rely on the parsedSymbolDecorator for bootstrap-plus. Please upgrade fitnesse, or feel free to roll your own plugin using the classes from the toolchian-plugin.

You can also try an older version of the plugin (pre 2020-05). It should not yet contain the table decorator styling

tcnh avatar Dec 09 '20 11:12 tcnh

yes I updated finesse and kept it in the then I am getting like the method not found I am using appian for fitnesse Screenshot (105)

neeraj5d5 avatar Dec 14 '20 06:12 neeraj5d5

There's a few things going wrong here:

  1. You are trying to use debug script. This requires Pause Test Fixture (from https://repo.maven.apache.org/maven2/nl/praegus/toolchain-fixtures/1.22/) to be included as a library.
  2. Conditional scenario is a table, to you cannot wrap it inside a script like this. Just use it as a scenario table and write the condition in the first cell. You can then call the scenario from a script table.

Please have a look at the readme at https://github.com/praegus/toolchain-fitnesse-plugin for usage instructions.

And be sure not to use Java 15 with FitNesse, as it has Nashorn removed and conditional script/scenario tables depend on it.

tcnh avatar Dec 14 '20 08:12 tcnh

hi is there any class that will screenshot when the table content fail

neeraj5d5 avatar Dec 19 '20 06:12 neeraj5d5

I think this is not the best place to get support on this, as it has no relation to the original issue/question and to answer it, one would need additional info. A better place to ask this type of questions is stack overflow, or the fitnesse community slack. A link to the inviter for the latter is in the footer of the fitnesse.org website.

tcnh avatar Dec 19 '20 10:12 tcnh