Concept

Concept

Fennec is the testers toolbox. The idea it to put all the things a tester needs in a single place, and to take advantage of any opportunity to enhance them.

Fennec ties together several testing related modules and enhances their functionality in ways you don't get when loading them individually. Fennec makes testing easier, and more useful. Areas Fennec effects are Concurrency, State, Workflow, Tools, and Mocking.

History

The concept for Fennec came about after seeing test frameworks not based on Test::Builder that had some desirable capabilities. Unfortunately these frameworks also lacked things that made Test::Builder desirable. Initially Fennec was designed as an independant framework that had capabilities gathered from many different frameworks.

With Fennec 1.000 the architecture was rewritten to be based off of Test::Builder. Other features were re-written to take advantage of Test::Builder and tools built from it. Finally several useful components of Fennec were broken up into additional distributions.

Fennec 2.000 Introduces some architecture changes that improve on design mistakes of 1.000. In addition it fixes bugs, and introduces handy new features.

Features

These are the key features of Fennec

Fennec also does the work of tying together several popular test modules reducing your boilerplate code.
Modules

Automatically loaded and imported

Test::More
The most popular testing module
Test::Exception
Used to test code that throws exceptions
Test::Warn
Test code that issues warnings
Test::Workflow
RSPEC and other workflow/state tools
Mock::Quick
Mocking that doesn't make you want to gouge your eyes out
Child
Forking at a higher level
Example
Vanilla
Here is a simple test file thats tests Data::Dumper.

(Vanilla Syntax)

Declare
Here is a simple test file thats tests Data::Dumper.

(Fennec::Declare Syntax)

Output
Here we can see the verbose output of the test file. Nothing here would be displayed without the -v flag to prove.
Note, we see the output for our 3 explicit tests, as well as 2 additional ones for our implicit tests. The codeblocks that surround our tests are tests as well, they fail if the codeblock dies or throws an exception.
Predictability

Predictability and randomness

There are 2 conflicting approaches to unit testing:
Consistent
Every run of the unit test should be the same so that any failure or success is reproducable.
Randomized
The unit test should check for bad interactions by running in a random order.
Both these policies have disadvantages, and if you must pick one I recommend 'Consistant'.

Fennec gives you both!

This mean that you can test interactions with a random run order. However the 'date as seed' policy means that on a given day the order will always be the same. Finally the ability to specify the seed lets you reproduce a success or failure from any previous run.
Selection

Running a subset of tests

Have you ever had to debug a broken test at the very end of a long running test file?

With Fennec most tests should be grouped together by functionality or subsystem. These groups will usually be contained in a 'tests' codeblock. With fennec you can specify the 'FENNEC_TEST' environment variable. This variable can be set to a test-block name, or a line number. The line number can be any line between the start and end of the block, this is useful for editor integration.

Vim Integration

Vim integration is easy. The following added to your vimrc lets you press F8 to run whatever fennec test block is under your cursor.
Arguments
class
Used to specify the name of the package your test file is validating. When this parameter is specified 3 things are done for you: The class is automatically loaded, the $CLASS variable is imported and contains the module name, and the class() subroutine is defined and returns the name.
utils
Load these modules instead of the default list. If you need to specify import arguments for any specific util class, you can use the class name as the key with an arrayref containing the arguments.
base
Load the specified module and make it the base class for your test class. This is much like 'parent' or 'base', but it may save you a line. Really it is a pointless feature left in for legacy code that actually used it.
parallel
How many test blocks can be run in parallel.
  • Default is 3
  • Set to 1 to fork for each test, but only run one at a time.
  • Set to 0 to prevent forking.
  • Set to any positive integer.
Only test blocks are parallelized:
collector_class
Specify which collector to use. Defaults to a Test::Builder based collector that uses temp files to funnel tests from child procs to the parent.

You generally won't need to specify this, unless you use a test infrastructure that is neither TAP nor Test::Builder based.

runner_class
Specify the runner class. You probably don't need this.
runner_params
Lets you specify arguments used when Fennec::Runner is initialized.
skip_without
Tell Fennec to skip the test file if any of the specified modules are missing. This is useful if you have a test for optional functionality that only works if another module is installed.
test_sort
  • random
  • sorted
  • ordered
  • sub { ... }

Examples: Code block accepts a list of Test::Workflow::Test objects.
with_tests
Load these modules that have reusable tests. Reusable tests are tests that are common to multiple test files.

Test: Common/Tests.pm:

Subclass
Sometimes a project may end up using a set of Fennec arguments in almost every test. Sometimes there are utility modules everyone on the project wants by default. When this happens it can be useful for a project to subclass Fennec or Fennec::Declare.