Commit 4b5db1bb by Gabriel Falcao

just some brainstorming

parent 87d8afbf
before 0.2.0:
* https://github.com/gabrielfalcao/lettuce/pull/224
* https://github.com/gabrielfalcao/lettuce/pull/255
* https://github.com/gabrielfalcao/lettuce/pull/256
* all the "high-priority" labeled tickets
* https://github.com/gabrielfalcao/lettuce/issues/103 (maybe)
* https://github.com/gabrielfalcao/lettuce/issues/198
......@@ -31,7 +31,7 @@ from lettuce.terrain import world
from lettuce.decorators import step
from lettuce.registry import call_hook
from lettuce.registry import STEP_REGISTRY
from lettuce.registry import STEP_REGISTRY, CASTER_REGISTRY
from lettuce.registry import CALLBACK_REGISTRY
from lettuce.exceptions import StepLoadingError
from lettuce.plugins import xunit_output
......
......@@ -24,7 +24,7 @@ from copy import deepcopy
from lettuce import strings
from lettuce import languages
from lettuce.fs import FileSystem
from lettuce.registry import STEP_REGISTRY
from lettuce.registry import STEP_REGISTRY, CASTER_REGISTRY
from lettuce.registry import call_hook
from lettuce.exceptions import ReasonToFail
from lettuce.exceptions import NoDefinitionFound
......@@ -110,16 +110,29 @@ class StepDefinition(object):
"""A step definition is a wrapper for user-defined callbacks. It
gets a few metadata from file, such as filename and line number"""
def __init__(self, step, function):
self.step = step
self.function = function
self.file = fs.relpath(function.func_code.co_filename)
self.line = function.func_code.co_firstlineno + 1
self.step = step
def _apply_casters(self, step, args):
sentence = step.sentence
if not args:
return ()
for regex, caster in CASTER_REGISTRY.items():
matched = re.search(regex, sentence)
if matched:
args = (caster(*args), )
return args
def __call__(self, *args, **kw):
"""Method that actually wrapps the call to step definition
callback. Sends step object as first argument
"""
try:
args = self._apply_casters(self.step, args)
ret = self.function(self.step, *args, **kw)
self.step.passed = True
except Exception, e:
......
......@@ -15,8 +15,43 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
from lettuce.core import STEP_REGISTRY
from lettuce.exceptions import StepLoadingError
from lettuce.core import STEP_REGISTRY, CASTER_REGISTRY
from lettuce.exceptions import CasterLoadingError, StepLoadingError
def caster(regex):
"""Decorates a function that will be responsible for casting
matched regex into object, leveraging more flexibility on step
definitions and simplifying the test ecosystem.
Example::
>>> from lettuce import step
>>> from models import Contact
>>>
>>> @step.caster(r'the person "(.*)"')
... def into_a_contact_object(value):
... return Contact.objects.get(name=value)
...
>>> @step(r'Given the person "(.*)" is erased from my address book')
... def given_i_do_something(step, user):
... user.delete()
Notice the name matched in the regex below being cast into a
`Contact` object
"""
def wrap(func):
try:
re.compile(regex)
except re.error, e:
raise CasterLoadingError("Error when trying to compile:\n"
" regex: %r\n"
" for function: %s\n"
" error: %s" % (regex, func, e))
CASTER_REGISTRY[regex] = func
return func
return wrap
def step(regex):
......@@ -36,6 +71,7 @@ def step(regex):
Notice that all step definitions take a step object as argument.
"""
def wrap(func):
try:
re.compile(regex)
......@@ -48,3 +84,5 @@ def step(regex):
return func
return wrap
step.caster = caster
......@@ -50,3 +50,8 @@ class LettuceSyntaxError(SyntaxError):
class StepLoadingError(Exception):
"""Raised when a step cannot be loaded."""
pass
class CasterLoadingError(Exception):
"""Raised when a caster's regex cannot be compiled."""
pass
......@@ -38,8 +38,8 @@ class CallbackDict(dict):
for callback_list in action_dict.values():
callback_list[:] = []
STEP_REGISTRY = {}
CASTER_REGISTRY = {}
CALLBACK_REGISTRY = CallbackDict(
{
'all': {
......@@ -91,4 +91,5 @@ def call_hook(situation, kind, *args, **kw):
def clear():
STEP_REGISTRY.clear()
CASTER_REGISTRY.clear()
CALLBACK_REGISTRY.clear()
# -*- coding: utf-8 -*-
# <Lettuce - Behaviour Driven Development for python>
# Copyright (C) <2010-2012> Gabriel Falcão <gabriel@nacaolivre.org>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import sys
from lettuce import step, world, Feature
from sure import that, scenario
FEATURE1 = '''
Feature: Transformations
Scenario: Simple matching
Given the following users:
| name | email |
| Gabriel | gabriel@lettuce.it |
| Lincoln | lincoln@comum.org |
When the user "Gabriel" is mentioned
Then it becomes available in `world` as "last_user"
And it is an `User` instance
'''
THIS_MODULE = sys.modules[__name__]
def step_runner_environ(context):
"Make sure the test environment is what is expected"
from lettuce import registry
registry.clear()
world.users = {}
class User:
def __init__(self, name, email):
self.name = name
self.email = email
def save(self):
world.users[self.name] = self
@step('Given the following users')
def collect_users(step):
for data in step.hashes:
user = User(**data)
user.save()
@step(r'When the user "(\w+)" is mentioned')
def mention_user(step, user):
world.last_user = user
@step(r'Then it becomes available.* as "([\w_]+)"')
def becomes_available(step, attribute):
assert (hasattr(world, attribute),
'world should contain the attribute %s' % attribute)
@step(r'And it is an `(\w+)` instance')
def and_is_instance_of(step, klass):
import ipdb;ipdb.set_trace()
assert that(world.last_user).is_a(klass)
@scenario(step_runner_environ)
def test_transformations(context):
@step.caster(r'the user "(\w+)" is mentioned')
def capture_users_by_name(name):
return world.users[name]
@step.caster(r'it is an `(\w+)`')
def get_class_by_name(name):
return THIS_MODULE[name]
f = Feature.from_string(FEATURE1)
feature_result = f.run()
scenario_result = feature_result.scenario_results[0]
assert that(scenario_result.steps_undefined).equals([])
assert that(scenario_result.steps_failed).equals([])
assert feature_result.passed
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment