Skip to content

Instantly share code, notes, and snippets.

@anentropic
Last active June 24, 2019 15:04
Show Gist options
  • Save anentropic/ab0423eed634c0d31b7df4663bbd16ad to your computer and use it in GitHub Desktop.
Save anentropic/ab0423eed634c0d31b7df4663bbd16ad to your computer and use it in GitHub Desktop.
Per-scenario test methods in Morelia
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