Fancordion is similar to Cucumber but focuses on readability and presentation. It embeds test results directly into your test documentation, giving it real meaning.
Features:
Pretty - creates beautiful HTML output.
Simple - run Fancordion tests with fant, just like a unit test!
Linkable - create organised and hierarchical index pages with the run command.
Extensible - write your own commands with ease.
Skinnable - Customise your HTML reports as you see fit.
1). Create a text file called HelloWorldFixture.fan
using afFancordion
** My First Fixture
** ################
**
** This is a simple Fancordion fixture that verifies that the method
** 'greeting()' returns 'Hello World!'.
**
** Example
** -------
** Fancordion says, [Hello World!]`verify:eq(greeting)`
**
class HelloWorldFixture : Test, FixtureTest {
Str greeting() {
"Hello World!"
}
}
2). Run HelloWorldFixture.fan as a Fantom test script ( fant ) from the command prompt:
Try changing Hello World! to something else and re-run the test to watch it fail.
Then have greeting() throw an Err... See the stacktrace embedded in the result!
Terminology
An Acceptance Test is a standard Fantom Test that has been enhanced to verify real user requirements.
The Fixture is the code part of the acceptance test that does the actual work.
Specification refers to the documentation part of the acceptance test.
Commands are special links in the specification that drive the test, specifying input and verifying output.
Usage
Any Fantom class annotated with the @Fixture facet can be run as a Fancordion fixture. Just pass it into FancordionRunner.runFixture():
using afFancordion
** My first Fancordion fixture.
@Fixture
class MyFixture {
...
}
fixture := MyFixture()
runner := FancordionRunner()
runner.runFixture(fixture)
FancordionRunner is designed to be subclassed and has several methods, or hooks, that change it's behaviour:
suiteSetup() is only ever called once no matter how many fixtures are run, or FancordionRunners created.
suiteTearDown() again is only ever called the once (currently in an Env shutdown hook).
fixtureSetup() is called before every fixture.
fixtureTearDown() is called after every fixture.
skinType & gimmeSomeSkin() determine & create an instance of the FancordionSkin class used to render the result HTML. You could, for instance, change this to use a Bootstrap skin.
outputDir is where the result files are saved.
commands is a map of all the Commands made available to the test. To extend Fancordion, simply add your own Command implementation to the map! (Super easy!)
To help you bridge the gap between Fancordion and standard Fantom tests, Fancordion ships with FixtureTest. This handy mixin lets you run any Fixture as a Fantom Test. (Note your test classes still have to extend Test as well.)
To use a specific FancordionRunner in your tests, override cancordionRunner() to return desired instance. Even though all your tests will extend FixtureTest, the fancordionRunner() method will only be called once. This means you can run a single test with fant, or all of them, and they will still only use the same runner instance.
By marking text in the specification as links, you turn them into commands. Your specification can now be thought of as a simple script.
When you run the specification script, the Fandoc is converted into HTML and the commands executed as they are encountered. The commands generate HTML markup to show whether they passed or failed.
By default the specification is assumed to be the doc comment on the fixture:
** This comment is the specification.
@Fixture
class MyFixture { }
By doing so, every line in the doc comment must start with a double asterisk **. The specification may exist in its own file, just give a URL to its location in the @Fixture facet:
** This comment is the specification.
@Fixture { specification=`/myproj/specs/Spec1.fandoc` }
class MyFixture { }
Specifications, when they exist in their own file, do not start each line with a double asterisk **.
TIP: Use Fandoc Viewer to edit fandoc files and specifications.
Specifications can be written in any way you wish, but the following structure is very useful. It is written here as a fandoc comment so you may cut and paste it into your specifications.
** Heading
** #######
** Give some background information and explain the problem at hand.
**
** As a...
** When I...
** I want...
**
** Example
** -------
** Now describe an example scenario and the expected behaviour. This will be the test:
**
** Given...
** When...
** Then...
**
** Only the example should contain commands.
**
** Further Details
** ===============
** - [link to other fixtures here]`run:OtherTest`
** - [that explain edge cases]`run:MoreTests`
Commands
set
The set command sets a field of the fixture to the value of the link text. The Str is coercered to the field's type.
** The meaning of life is [42]`set:number`.
@Fixture
class ExampleFixture {
Int? number
}
execute
The execute command calls a method on the fixture. The cmd is compiled as Fantom code so may be any valid Fantom code.
Any occurrences of the token #TEXT are replaced with the command / link text.
** [The end has come.]`execute:initiateShutdownSequence(42, #TEXT, "/tmp/end.txt".toUri)`
@Fixture
class ExampleFixture {
Void initiateShutdownSequence(Int num, Str cmdText, Uri url) {
...
}
}
verify
The verify command executes a Test verify method against the link text. Available verify methods are:
eq(...)
notEq(...)
type(...)
true(...)
false(...)
null(...)
notNull(...)
Arguments to the verify methods are run against the fixture and may be any valid Fantom code.
** The meaning of life is [42]`verify:eq(number)`.
@Fixture
class ExampleFixture {
Int? number := 43
}
Arguments for the eq() and notEq() methods are type coerced to a Str. Arguments for the true() and false() are type coerced to a Bool.
fail
This simple command fails the test with the given message.
** The meaning of life is [42]`fail:TODO`.
@Fixture
class ExampleFixture { }
run
The run command runs another Fancordion fixture and prints an appropriate success / failure link to it.
The command path must be the name of the Fixture type to run. The fixture type may be qualified.
Use run commands to create a specification containing a list of all acceptance tests for a feature, in a similar way you would use a test suite.
You could even nest specifications to form a hierarchical index, with results aggregated to display a single green / red / grey result.
** Questions:
** - [Why is the sky blue?]`run:BlueSkyFixture`.
@Fixture
class ExampleFixture { }
link
The link command renders a standard HTML <a> tag. It is added with the file, http, https and mailto schemes.
** Be sure to check out [Fantom-Factory]`http://www.fantomfactory.org/`.
@Fixture
class ExampleFixture { }
embed
The embed command executes the given function against the fixture and embeds the results as raw HTML.
Use it to add extra markup to your fixtures.
** Kids, don't play with [FIRE!]`embed:danger(#TEXT)`.
@Fixture
class ExampleFixture {
Str danger(Str text) {
"""<span class="danger">${text}</span>"""
}
}
Test BedSheet Apps
Fancordion can be used to test BedSheet applications.
Typically I would start the web application under test (via Bounce) in the runner's suiteSetup(). Since all web application state is (usually) stored in a database, there is little need to re-start the web app for every test. While this only saves you a couple of seconds, over the course of many tests it can add up to be quite a time saver!
Web application shutdown would then occur in the runner's suiteTearDown() method.
Below shows a typical FancordionRunner setup together with an abstract WebFixture class.
using afBounce
using afFancordion
class MyFancordionRunner : FancordionRunner {
private BedServer? server
new make(|This|? f := null) : super(f) {
outputDir = `fancordion-results/`.toFile
// other runner configuration...
}
override Void suiteSetup() {
super.suiteSetup
server = BedServer(AppModule#.pod).addModule(WebTestModule#).startup
}
override Void suiteTearDown(Type:FixtureResult resultsCache) {
server?.shutdown
super.suiteTearDown(resultsCache)
}
override Void fixtureSetup(Obj fixtureInstance) {
webFixture := ((WebFixture) fixtureInstance)
super.fixtureSetup(fixtureInstance)
webFixture.client = server.makeClient
server.injectIntoFields(webFixture)
webFixture.fixtureSetup()
}
override Void fixtureTearDown(Obj fixtureInstance, FixtureResult result) {
webFixture := ((WebFixture) fixtureInstance)
webFixture.fixtureTearDown
super.fixtureTearDown(fixtureInstance, result)
}
}
class WebTestModule {
@Contribute { serviceType=ServiceOverrides# }
static Void contributeServiceOverride(MappedConfig config) {
config["IocEnv"] = IocEnv.fromStr("Testing")
}
// other test specific services and overrides...
}
// The super class for all Web Fixtures
abstract class WebFixture : Test, FixtureTest {
BedClient? client
virtual Void fixtureSetup() { }
virtual Void fixtureTearDown() { }
// The important bit - this creates the FancordionRunner to be used.
override FancordionRunner fancordionRunner() {
MyFancordionRunner()
}
// Other common / reusable methods such as :
// loginAs(...), logout(), gotoPage(...), etc...
}
SlimerDude Thu 31 Jul 2014
Fancordion Preview Release!
Transform your boring unit tests into beautiful specification documents
Fancordion
is similar to Cucumber but focuses on readability and presentation. It embeds test results directly into your test documentation, giving it real meaning.Features:
run
command.For a live example of Concordion results, see the output from the Java Concordion framework.
Quick Start
1). Create a text file called
HelloWorldFixture.fan
2). Run
HelloWorldFixture.fan
as a Fantom test script ( fant ) from the command prompt:3). View the generated fixture result file:
The green highlight means the test passed.
Try changing
Hello World!
to something else and re-run the test to watch it fail.Then have
greeting()
throw an Err... See the stacktrace embedded in the result!Terminology
An Acceptance Test is a standard Fantom Test that has been enhanced to verify real user requirements.
The Fixture is the code part of the acceptance test that does the actual work.
Specification refers to the documentation part of the acceptance test.
Commands are special links in the specification that drive the test, specifying input and verifying output.
Usage
Any Fantom class annotated with the @Fixture facet can be run as a Fancordion fixture. Just pass it into
FancordionRunner.runFixture()
:FancordionRunner is designed to be subclassed and has several methods, or hooks, that change it's behaviour:
suiteSetup()
is only ever called once no matter how many fixtures are run, orFancordionRunners
created.suiteTearDown()
again is only ever called the once (currently in an Env shutdown hook).fixtureSetup()
is called before every fixture.fixtureTearDown()
is called after every fixture.skinType
&gimmeSomeSkin()
determine & create an instance of theFancordionSkin
class used to render the result HTML. You could, for instance, change this to use a Bootstrap skin.outputDir
is where the result files are saved.commands
is a map of all the Commands made available to the test. To extend Fancordion, simply add your own Command implementation to the map! (Super easy!)To help you bridge the gap between Fancordion and standard Fantom tests, Fancordion ships with FixtureTest. This handy mixin lets you run any Fixture as a Fantom Test. (Note your test classes still have to extend
Test
as well.)To use a specific
FancordionRunner
in your tests, overridecancordionRunner()
to return desired instance. Even though all your tests will extendFixtureTest
, thefancordionRunner()
method will only be called once. This means you can run a single test with fant, or all of them, and they will still only use the same runner instance.Specifications
Specifications are documents written in Fantom's own Fandoc format, similar to Markdown and Almost Plain Text.
By marking text in the specification as links, you turn them into commands. Your specification can now be thought of as a simple script.
When you run the specification script, the Fandoc is converted into HTML and the commands executed as they are encountered. The commands generate HTML markup to show whether they passed or failed.
By default the specification is assumed to be the doc comment on the fixture:
By doing so, every line in the doc comment must start with a double asterisk
**
. The specification may exist in its own file, just give a URL to its location in the@Fixture
facet:Specifications, when they exist in their own file, do not start each line with a double asterisk
**
.Specifications can be written in any way you wish, but the following structure is very useful. It is written here as a fandoc comment so you may cut and paste it into your specifications.
Commands
set
The
set
command sets a field of the fixture to the value of the link text. TheStr
is coercered to the field's type.execute
The
execute
command calls a method on the fixture. The cmd is compiled as Fantom code so may be any valid Fantom code.Any occurrences of the token
#TEXT
are replaced with the command / link text.verify
The
verify
command executes a Test verify method against the link text. Available verify methods are:Arguments to the verify methods are run against the fixture and may be any valid Fantom code.
Arguments for the
eq()
andnotEq()
methods are type coerced to aStr
. Arguments for thetrue()
andfalse()
are type coerced to aBool
.fail
This simple command fails the test with the given message.
run
The
run
command runs another Fancordion fixture and prints an appropriate success / failure link to it.The command path must be the name of the Fixture type to run. The fixture type may be qualified.
Use
run
commands to create a specification containing a list of all acceptance tests for a feature, in a similar way you would use a test suite.You could even nest specifications to form a hierarchical index, with results aggregated to display a single green / red / grey result.
link
The
link
command renders a standard HTML <a> tag. It is added with thefile
,http
,https
andmailto
schemes.embed
The
embed
command executes the given function against the fixture and embeds the results as raw HTML.Use it to add extra markup to your fixtures.
Test BedSheet Apps
Fancordion can be used to test BedSheet applications.
Typically I would start the web application under test (via Bounce) in the runner's
suiteSetup()
. Since all web application state is (usually) stored in a database, there is little need to re-start the web app for every test. While this only saves you a couple of seconds, over the course of many tests it can add up to be quite a time saver!Web application shutdown would then occur in the runner's
suiteTearDown()
method.Below shows a typical FancordionRunner setup together with an abstract WebFixture class.
Have fun!