Last active
June 24, 2019 15:04
-
-
Save anentropic/ab0423eed634c0d31b7df4663bbd16ad to your computer and use it in GitHub Desktop.
Per-scenario test methods in Morelia
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import re | |
import unicodedata | |
from django.conf import settings | |
from morelia import run | |
from morelia.grammar import Scenario | |
from morelia.parser import Parser | |
FEATURES_PATH = 'tests/features' | |
def _get_scenario_titles(feature_path): | |
""" | |
Extract the titles of the scenarios in the feature at `feature_path` | |
Args: | |
feature_path (str): path to a gherkin feature file | |
Returns: | |
Set[str] | |
""" | |
with open(feature_path) as f: | |
feature = f.read() | |
parser = Parser() | |
nodes = parser.parse_feature(feature) | |
return {node.predicate for node in nodes if isinstance(node, Scenario)} | |
def _normalise(value): | |
""" | |
(adapted from django `slugify` but using underscores instead of hyphens) | |
Args: | |
value (str) | |
Returns: | |
str | |
""" | |
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore') | |
value = re.sub(r'[^\w\s-]', '', value).strip().lower() | |
return re.sub(r'[-\s]+', '_', value) | |
def _scenario_test_method_factory(feature_path, scenario): | |
""" | |
Generate a test_x method for a scenario in the feature at `feature_path` | |
NOTE: | |
morelia `run` matches scenarios by title using `scenario` as a regex | |
Args: | |
feature_path (str): path to a gherkin feature file | |
scenario (str): title of a scenario | |
Returns: | |
Callable[[], None] | |
""" | |
def test_method(self): | |
run( | |
filename=feature_path, | |
suite=self, | |
scenario=re.escape(scenario), | |
verbose=True, | |
) | |
return test_method | |
class BehaviourTestMixinMetaclass(type): | |
""" | |
Parses the specified feature file and generates a test_x method for each | |
scenario found. | |
""" | |
def __new__(cls, name, bases, attrs): | |
feature_file = attrs.pop('feature_file', None) | |
if feature_file is not None: | |
feature_path = os.path.join( | |
settings.PROJECT_PATH, FEATURES_PATH, feature_file | |
) | |
scenarios = _get_scenario_titles(feature_path) | |
for scenario in scenarios: | |
method_name = 'test_%s' % _normalise(scenario) | |
test_method = _scenario_test_method_factory(feature_path, scenario) | |
test_method.__name__ = method_name | |
attrs[method_name] = test_method | |
return type.__new__(cls, name, bases, attrs) | |
class BehaviourTestMixin(object): | |
""" | |
Usage: | |
class MyFeatureBehaviourTests(BehaviourTestMixin, TestCase): | |
feature_file = "section/myfeature.feature" | |
... step methods ... | |
""" | |
__metaclass__ = BehaviourTestMixinMetaclass |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment