fitnesse
fitnesse copied to clipboard
Support conditional execution of script/scenario tables
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:
- 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. Iftrue
, scenario/script table processing will terminate immediately. - 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!
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 ;-)
Hi Tom:
This is really close to what we need!
There are two issues:
- 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. - 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 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: 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
)?
Yes, that's the same approach. I'll have a look if can easily add a conditional scenario table
Thanks so much Tom. That would be so helpful for us.
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 :)
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?
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, 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.
The SliMExpressionEvaluator uses Nashorn, which is deprecated, bus afaik still present until Java 14. What version of Java are you using to run?
@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"
@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
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.
Not works still. However, used apache.commons.jexl3 to calculate Expression than javax.script.ScriptEngine, and it's OK for me.
hi how to add the plugins after downloading jars
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/
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
I think your FitNesse version is outdated. Are you using 20200501?
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
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
yes I updated finesse and kept it in the then I am getting like the method not found I am using appian for fitnesse
There's a few things going wrong here:
- 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.
- 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.
hi is there any class that will screenshot when the table content fail
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.