Search code examples
pythonpython-behave

Why is ActiveTagMatcher is ignoring some of the values I'm passing to it?


I've been successfully using ActiveTagMatcher to tag some tests that should be run under some conditions, but for some reason ActiveTagMatcher is ignoring some of the values I set. I've boiled it down to this example:

features/test.feature:

Feature: foo

# This does not work!
@only.with_variable_one=True
Scenario: variable one must be True
  Then something

# This works fine.
@only.with_variable_two=foo
Scenario: variable two must be foo
  Then something

# Try to see if shortening it to something that does not use the `=` works.
# It does not work either.  
@only.with_variable_one
Scenario: variable two must be foo
  Then something

features/environment.py:

import os

from behave.tag_matcher import ActiveTagMatcher

def before_all(context):
    condition = context.config.userdata.get("condition", None) is not None
    values = {
        "variable_one": condition,
        "variable_two": "foo"
    }
    context.active_tag_matcher = ActiveTagMatcher(values)

def before_scenario(context, scenario):
    if context.active_tag_matcher.should_exclude_with(scenario.effective_tags):
        scenario.skip(reason="Disabled by an active tag")
        return

I have a features/steps/something.py which contains just enough to avoid complaints about the step being undefined:

@then(u"something")
def step_impl(context):
    pass

Running behave should only skip the 1st scenario only if I do not invoke it with -D condition, but it always skips the 1st scenario! Conversely, it is never skipping the last scenario!

I'm using Behave 1.2.5.


Solution

  • Quick Answer

    Make sure all your values in your values dictionary are strings:

    values = {
        "variable_one": str(condition),
        "variable_two": "foo"
    }
    

    And @only.with_variable_one for your last scenario cannot work. You must have an equal-sign and a value for the tag to work with ActiveTagMatcher.

    Longer Explanation

    Let's start with the easy bit. The tag @only.with_variable_one is simply not recognized by the ActiveTagMacher at all. There exist other tags that don't need to use an operator to indicate that they should be active if the condition they correspond to is true. For instance, you write @wip when you want to use Behave's builtin work-in-progress feature, not @wip=True. However, the ActiveTagMacher requires that there be an equal sign in the tag that you use, otherwise it does not recognize it. So @only.with_variable_one is flat out ignored.

    The reason @only.with_variable_one=True is not working for you is because ActiveTagMatcher's parsing of the tag will result in reading True as the string "True" whereas in your values dictionary you pass a the boolean value True. ActiveTagMacher uses operator.eq to check for equality. So the test is performs is equivalent to "True" == True, which is false in Python. So the equality check always fails and thus ActiveTagMacher always excludes your scenario. You can fix this by converting your boolean to a string:

    values = {
        "variable_one": str(condition),
        "variable_two": "foo"
    }
    

    In fact, every single value in your values dictionary must be a string. For instance, if you have a number in there, you must also convert it to a string. Otherwise, tests that are performed against that number won't ever be true.