Cellophane

Cellophane is a thin wrapper around Cucumber that is intended to make it easier to structure projects in ways that make sense to the people working on them. It has been tested with Cucumber 0.10.0 on Ruby 1.8.7-p302 and Ruby 1.9.2-p0.

Installation

gem install cellophane

Configuration

Project-specific File

Cellophane can be configured project by project through the use of a .cellophane.yaml file that lives in the root of your project. Configurable options are:

Directive cuke_command

Explanation Tells Cellophane how to call Cucumber. Defaults to cucumber.

Examples cuke_command: bundle exec cucumber cuke_command: script/cucumber cuke_command: cuke (as in a shell alias)

Directive cucumber

Explanation Options you want to pass to Cucumber by default.

Example cucumber: --format progress --no-profile

Notes Anything defined by cucumber in the configuration file will be overwritten by the -c/--cucumber command line switch (see below).

Directive feature_path

Explanation Root location of your feature files. Defaults features.

Example feature_path: cuke/features

Notes The path is relative to your project’s root.

Directive step_path

Explanation Location of your step definitions. Defaults to features/step_definitions. It can be defined in two ways:

  1. a path relative to the project root
  2. nested in each feature directory

Examples


step_path: cuke/steps
step_path:
  nested_in: step_definition

Notes Use the first method if your step defitions follow the structure of your features. For example:


my_project
	- app
	- config
	- cuke
		- features
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- steps
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- support
	- db
	- lib
	- etc, etc

Use the second method if each feature directory has its own step definitions directory:


my_project
	- app
	- config
	- features
		- admin
			- step_definitions
		- user
			- step_definitions
		- visitor
			- step_definitions
		- support
	- db
	- lib
	- etc, etc

Directive shared

Explanation Automatically load steps named shared_steps.rb in all directories that comprise the step path for the current feature file. Defaults to true. This option allows you to share steps among features while keeping them organized according to scope.

Examples shared: global # instead of looking for shared_steps.rb, look for global_steps.rb shared: false # don’t automatically require shared steps

Notes Consider the following project structure:


my_project
	- app
	- config
	- cuke
		- features
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- steps
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- support
	- db
	- lib
	- etc, etc

When you run a feature in features/admin/reports/weekly.feature, Cellophane will automatically require shared steps found in the following locations:

steps/admin/reports/shared_steps.rb # steps specific to admin reports steps/admin/shared_steps.rb # steps specific to admin steps/shared_steps.rb # steps used by the whole application

If your steps are nested, Cellophane will look for shared steps only in the nested step location. For example, if your project looks like


my_project
	- app
	- config
	- features
		- admin
			- step_definitions
		- user
			- step_definitions
		- visitor
			- step_definitions
		- support
	- db
	- lib
	- etc, etc

and you run a feature in features/admin/email.feature, Cellophane will look for features/admin/step_definitions/shared_steps.rb.

Directive requires

Explanation Other directories or files that Cucumber needs to require. It is an array.

Examples requires: [cuke/support, cuke/steps/shared]


requires:
  - cuke/support
  - cuke/steps/shared

Notes requires are passed to Cucumber as defined, so absolute paths are respected.

Command Line

Cellophane has the following command line options:


Usage: cellophane [options] PATTERN
    -r, --regexp                     PATTERN is a regular expression. Default is false.
    -t, --tags TAGS                  Tags to include/exclude.
    -c, --cucumber OPTIONS           Options to pass to cucumber.
    -p, --print                      Echo the command instead of calling cucumber.
    -d, --debug                      Require ruby-debug.
    -v, --version                    Display the version.
    -h, --help                       Display this screen.

PATTERN is the pattern of feature files that you are interested in. By default PATTERN is a glob, but by using the -r/--regexp switch, you can pass a Ruby regular expression instead (no slashes necessary). When using a glob, you can specify that files matching the pattern are to be excluded by preceeding the pattern with a tilde (~). You can also combine include and exclude patterns in the same call. See below for examples.

If you need to pass something through to Cucumber, such as a formatter, use the -c/--cucumber switch. Depending on what it is you are passing through, you might need to enclose it in quotes. Do keep in mind that -c/--cucumber on the command line overrides that defined in the YAML file.

-d/--debug is only useful for Cellophane development and will have no effect on Cucumber.

History

By default, Cucumber expects features to live in features and step definitions to live in features/step_definitions. When running a feature, all step definitions are loaded unless you explicitly require just the ones you want. If you structure your project differently, you have to require files explicitly, which gets old real fast.

After some experimenting with different directory structures, I settled on the following for my own projects:


my_project
	- app
	- config
	- cuke
		- features
		- steps
		- support
	- db
	- lib
	- etc, etc

It’s based on the structure of the spec directory and I find that it fits my brain pretty well. There are three organizational strategies that I found myself following:

  1. separation of features in subdirectories
  2. keeping feature specific steps in files that are named according to the feature
  3. keeping all shared steps in files that are named in a more generalized fashion

For example, a project my look like this:


my_project
	- app
	- config
	- cuke
		- features
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- steps
			- admin
				- reports
				- user_maintenance
			- user
				- communication
				- profile
		- support
	- db
	- lib
	- etc, etc

If the features in cuke/features/admin/reports had any steps that were specific to them, they would live cuke/steps/admin/reports. Any steps that are shared between two or more features would go in files in cuke/support.

To use this structure in Cucumber requires explicitly requiring the files/directories with every run. To run all of the features in cuke/features/admin/reports, I would enter the command


cucumber -r cuke/support -r cuke/steps/admin/reports cuke/features/admin/reports

I got weary of doing that all the time, so I experimented with aliases and profiles, neither of which really met my needs. So I created Cellophane. With it, the same features could be as easy as:


cellophane admin/*

As time went on, more patterns began to emerge from my workflow, and with them, Cellophane gained abilities.

Examples and Notes

Features

Using the example project above:


# run all features
cellophane

# run everything in admin
cellophane admin/*

# run everything in admin, except the reports
cellophane admin/*,~admin/reports/*

# run everything, except the admin reports
cellophane ~admin/reports/*

# run all user_maintenance features regardless of where they are
cellophane */user_maintenance/*

# run all features in files with email in the name
cellophane **/*email*

# or use a regular expression
cellophane -r email

Step Definitions

Cellophane does assume that your steps are defined in files that follow the naming of your features. For each feature file that is found, Cellophane will look for a step file with the same name and automatically require it. To require shared step definitions, put them in a folder and include that folder in the requires section of .cellophane.yaml. Also look into the shared directive of the configuration file, which allows more fine-grained control of shared steps.

For example, if you have a feature defined in <feature path>/admin/user/account_maintenance.feature, Cellophane will automatically require a step definition file located in <step path>/admin/user/account_maintenance_steps.rb. Unless you are using nested step definitions in .cellophane.yaml (step_path: {nested_in: steps }, in which case Cellophane will automatically require <feature path>/admin/user/steps/account_maintenance_steps.rb if it exists.

Tags

You don’t need to use @. You can if you want, but it’s not necessary.

Cellophane supports OR, AND, and NOT tags. Examples will probably illustrate better than words.

OR


cellophane -t one,two
cucumber -t @one,@two

AND


cellophane -t one,+two
cucumber -t @one -t @two

NOT


cellophane -t one,~two
cucumber -t @one -t ~@two

Mixed tags in a logical order


cellophane -t one,two,~three,+four
cucumber -t @one,@two -t @four -t ~@three

Mixed tags not in a logical order


cellophane -t +four,one,~three,two
cucumber -t @one,@two -t @four -t ~@three

I like to tag my scenarios with numeric values so I can easily run a specific scenario at any give time. It may sound a bit unusual, but I find it very handy.

Numeric OR ranges


cellophane -t 1-3
cucumber -t @1,@2,@3

Numeric NOT ranges


cellophane -t ~1-3
cucumber -t ~@1 -t ~@2 -t ~@3

Numeric OR range with a NOT


cellophane -t 1-3,~2
cucumber -t @1,@3