Feature: Use a user-defined Formatter As a behave user I want to be able to provide own, user-defined formatters So that I am able to add a new, user-defined formatters when I need other output formats (or override existing ones). . SPECIFICATION: . * A user-defined formatter must inherit from the "Formatter" class. . * A user-defined formatter can be specified on command-line . by using a scoped class name as value for the '--format' option. . * A user-defined formatter can be registered by name . by using the "behave.formatters" section in the behave config file. . . SCOPED CLASS NAME (for formatter.class): . * my.module_name:ClassName (preferred: single colon separator) . * my.module_name::ClassName (alternative: double colon separator) @setup Scenario: Feature Setup Given a new working directory And a file named "features/steps/passing_steps.py" with: """ from behave import step @step('{word:w} step passes') def step_passes(context, word): pass @step('{word:w} step fails') def step_fails(context, word): assert False, "XFAIL-STEP" """ And a file named "features/passing.feature" with: """ Feature: Scenario: Alice Given a step passes When another step passes Scenario: Bob Then some step passes """ And an empty file named "behave_ext/__init__.py" And a file named "behave_ext/formatter_one.py" with: """ from behave.formatter.base import Formatter class NotAFormatter(object): pass class SuperFormatter(Formatter): name = "super" description = "Super-duper formatter." """ And a file named "behave_ext/formatter_foo.py" with: """ from behave.formatter.base import Formatter class FooFormatter(Formatter): name = "foo" description = "User-specific FOO formatter." class FooFormatter2(Formatter): description = "User-specific FOO2 formatter." """ And a file named "behave_ext/formatter_bar.py" with: """ from behave.formatter.base import Formatter class BarFormatter(Formatter): description = "User-specific BAR formatter." """ @use_formatter.class Scenario: Use a known, valid user-defined formatter with scoped class name When I run "behave -f behave_ext.formatter_one:SuperFormatter features/passing.feature" Then it should pass with: """ 2 scenarios passed, 0 failed, 0 skipped 3 steps passed, 0 failed, 0 skipped, 0 undefined """ @use_formatter.class Scenario: Use a known, valid user-defined formatter (with double colon) ALTERNATIVE: Can currently use a double colon as separator, too. When I run "behave -f behave_ext.formatter_one::SuperFormatter features/passing.feature" Then it should pass with: """ 2 scenarios passed, 0 failed, 0 skipped 3 steps passed, 0 failed, 0 skipped, 0 undefined """ @use_formatter.class Scenario Outline: Use built-in formatter "<formatter.name>" like a user-defined formatter When I run "behave -f <formatter.class> features/passing.feature" Then it should pass with: """ 2 scenarios passed, 0 failed, 0 skipped 3 steps passed, 0 failed, 0 skipped, 0 undefined """ Examples: | formatter.name | formatter.class | | plain | behave.formatter.plain:PlainFormatter | | pretty | behave.formatter.pretty:PrettyFormatter | @problematic @use_formatter.class Scenario Outline: Use a problematic user-defined formatter (<case>) When I run "behave -f <formatter.class> features/passing.feature" Then it should fail with: """ error: format=<formatter.class> is unknown """ Examples: | formatter.class | case | | my.unknown_module:SomeFormatter | Unknown module | | behave_ext.formatter_one:UnknownClass | Unknown class | | behave_ext.formatter_one:NotAFormatter | Invalid Formatter class | @formatter.registered_by_name Scenario Outline: Register user-defined formatter by name: <formatter.name> Given a file named "behave.ini" with: """ [behave.formatters] foo = behave_ext.formatter_foo:FooFormatter foo2 = behave_ext.formatter_foo:FooFormatter2 bar = behave_ext.formatter_bar:BarFormatter """ And note that "the schema: 'formatter.name = formatter.class' is used" When I run "behave -f <formatter.name> features/passing.feature" Then it should pass with: """ 2 scenarios passed, 0 failed, 0 skipped 3 steps passed, 0 failed, 0 skipped, 0 undefined """ But the command output should not contain: """ error: format=<formatter.name> is unknown """ Examples: | formatter.name | Comment | | foo | First user-defined, registered formatter. | | bar | Last user-defined, registered formatter. | @formatter.registered_by_name Scenario: Help-Format shows description of registered-by-name formatters Given a file named "behave.ini" with: """ [behave.formatters] foo = behave_ext.formatter_foo:FooFormatter bar = behave_ext.formatter_bar:BarFormatter """ When I run "behave -f help" Then it should pass with: """ Available formatters: """ And the command output should contain: """ bar User-specific BAR formatter. """ And the command output should contain: """ foo User-specific FOO formatter. """ @problematic @formatter.registered_by_name Scenario Outline: Use problematic, registered-by-name formatter: <case> Given a file named "behave.ini" with: """ [behave.formatters] <formatter.name> = <formatter.class> """ When I run "behave -f <formatter.name> features/passing.feature" Then it should fail with: """ error: format=<formatter.name> is unknown """ Examples: | formatter.name | formatter.class | case | | unknown1 | my.unknown_module:SomeFormatter | Unknown module | | unknown2 | behave_ext.formatter_one:UnknownClass | Unknown class | | invalid1 | behave_ext.formatter_one:NotAFormatter | Invalid Formatter class |