NAME

Fennec::DeveloperManual::Workflows - Writing custom workflow plugins for Fennec.

WHY WRITE A WORKFLOW

In fennec a workflow is a way to structure tests. There are many popular testing frameworks that owe their popularity to how they structure their tests. A notable example is Ruby's RSPEC. Structured tests can simplify your job as a tester.

Within a workflow you can define child workflows, and groups of tests. Ultimately your workflow will return a list of testset objects. You can be as creative as you want in designing your workflow. You can also subclass Fennec::TestSet or use Fennec::TestSet::SubSet to wrap groups with buildup or teardown functions.

Using the Fennec::Workflow system it was a minimal effort to write an RSPEC like workflow. Less than 100 lines of code in SPEC.pm at the time this was written. If you do not like how Fennec implements SPEC you can probably implement it your own quite easily.

OVERVIEW

When a test file is loaded Fennec::Runner creates a root workflow, this root workflow is an Fennec::Workflow object. Every testset or workflow defined in the test file is a blessed method. The blessed methods are passed to a parent workflow. Those defined outside of a workflow are passed to a 'root' workflow.

A test classes root workflow is stored inthe classes meta object. Calling $test_class->fennec_meta() will return the meta object for a testclass. The meta object has 2 methods for workflows: $meta->root_workflow() which always returns the root workflow, and $meta->workflow() which returns the highest workflow on the stack, also known as the 'current' workflow.

Any testsets or workflows defined inside the method of a parent workflow are passed to that parent workflow. Each workflow object must inherit or implement the add_item() method which takes a workflow or testset. Testsets are retrieved from the workflows after they have been traversed to the deepest level.

Example:

package MyTest;
use Fennec workflows => [ 'MyWorkflow' ];

my_workflow parent => sub {
    tests 'parent tests' => {
        ...
    };
    my_workflow nested => sub {
        tests 'child tests' => {
            ...
        };
    };
};

1;

When Fennec runs it will load the file, it will create a root workflow, the workflow sub named 'parent' will be blessed and passed to the root workflow as a child workflow. The workflow sub 'nested' will be blessed as a workflow and passed to 'parent'. While this is going on the testsets will also be blessed and passed into their parent workflows.

CUSTOM WORKFLOW SYNOPSIS

package Fennec::Workflow::MyWorkflow;
use strict;
use warnings;

use Fennec::Workflow qw/:subclass/;

build_hook { ... };

build_with 'my_workflow';

sub testsets {
    my $self = shift;
    ...
    return @testsets;
}

sub add_item {
    my $self = shift;
    my ($item) = @_;

    if ( we_handle( $item )) { ... }
    else { $self->SUPER::add_item( $item )}
}

1;

METHODS THAT CAN BE OVERRIDEN

Overriding these is optional, not overriding them will result in a workflow that acts just like the root workflow.

@testsets = $wf->testsets()

testsets() should return an array with 0 or more testset objects, or objects that subclass testset. It is also responsible for returning the testsets from child workflows. If this method does not get testsets from child workflows they will not be run (which will generate a warning).

$wf->build()

The inherited method sets $wf as the current workflow, it then runs the method that was blessed into as the workflow object. Overriding this is not recommended, but may be necessary for some complicated workflows.

You should always call $self->built(1) at the end of your custom build() method. Not doing this may cause problems with items being added to a workflow AFTER build().

$wf->build_children()

This should rarely need to be overriden, calls $child->build() on all child workflows. If you override add_items to add items other than workflows and testsets, or to disallow adding items at all then you will probably want to override this to reflect the change.

$wf->add_item( $item )

Add $item to the workflow. The inherited method will add workflows or testsets, it will throw an exception for anything else. You can access the added items via $self->_workflows() and $self->_testsets().

If you override this method you should die with a useful message if $self->built() is true.

HELPFUL FUNCTIONS

build_with( $name )
build_with( $name, $class )

Exports a method ($name) that when called will create an instance of $class, or the class in which build_with() was called. The exported method will take a name and codeblock as arguments. After being build the instance will be added to the current in which it was defined.

build_hook { ... }
build_hook( sub { ... })

Add code that should be run just after building the workflows and just before running the tests.

OTHER METHODS TO KNOW

import()

Fennec::Workflow has a complicated import() method, in order to simplify it all classes that sublcass Fennec::Workflow have a new import() method exported to their package. It is important that you do not try to override import(), or that you are at least aware that you cannot call $wf->SUPER::import() and get the expected behavior. Defining your own import() method will also throw a redefine warning.

@wfs = $wf->workflows()

Returns a list of all the workflows added as children.

@testsets = $wf->testsets()

Returns a list of all the testsets in this workflow, and all of its children.

$tf = $wf->testfile()

Returns the Fennec::TestFile object currently being run.

$pwf = $wf->parent()

Returns the parent workflow object to which this one is a child, the root workflow will return the TestFile object.

$testsets = $wf->_testsets()
$wf->_testsets( \@testsets )

Get/Set the list of testsets, if you override add_item() and never caller SUPER::add_item() then you will need to manually add TestSets to the arrayref returned by _testsets().

$workflows = $wf->_workflows()
$wf->_workflows( \@workflows )

Get/Set the list of workflows, if you override add_item() and never caller SUPER::add_item() then you will need to manually add Workflows to the arrayref returned by _workflows().

run_tests()

In a normal Fennec run this will only be called on the root Workflow object. Overriding this in your subclass will have NO EFFECT.

$wf->add_items( @items )

Calls $wf->add_item() for each item in @items.

RECOMMENDED DOCUMENTATION

Fennec::Workflow

Documentation for the root workflow object, documentes inherited methods.

Fennec::TestSet

Your workflow would not be very useful if it didn't return TestSet objects. You should familiarise yourself with them.

Fennec::TestSet::SubSet

This is a subclass of TestSet which lets you group TestSets together with setups and teardowns wrapped around them.

USER DOCUMENTATION

User documentation is for those who wish to use Fennec to write simple tests, or manage a test suite for a project.

Fennec::UserManual

DEVELOPER DOCUMENTATION

Developer documentation is for those who wish to extend Fennec, or contribute to overall Fennec development.

Fennec::DeveloperManual

API DOCUMENTATION

API Documentation covers object internals. See the POD within each individual module.

AUTHORS

Chad Granum exodist7@gmail.com

COPYRIGHT

Copyright (C) 2010 Chad Granum

Fennec is free software; Standard perl licence.

Fennec is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.