spyoungtech / behave-webdriver

Selenium webdriver step library for use with the behave BDD testing framework
MIT License
62 stars 21 forks source link

String interpolation for step parameters #60

Closed urosn74 closed 5 years ago

urosn74 commented 5 years ago

Hi Spencer,

Thanks for super useful library. I have implemented a small extension to support our use case - it adds simple string interpolation capability for parameter values that allows us to efficiently reuse features across different environments.

I would appreciate if you will consider integrating this pull request into your base and eventually publish new release.

Best regards, Uroš

coveralls commented 5 years ago

Coverage Status

Coverage increased (+0.7%) to 98.373% when pulling 715dde9c84ba02b78fab541978d8d0491757a442 on urosn74:master into d085d22ee019786926d9ddb3d419b18c9dfbffe9 on spyoungtech:master.

spyoungtech commented 5 years ago

Hi @urosn74 thank you so much for taking the time to contribute back your improvements to this library!

At a quick glance, I like what I see. I definitely see how this adds a new degree of flexibility and re-usability for step definitions.

I'm a bit slammed at the moment, but I hope to get around to reviewing this soon; probably this week!

spyoungtech commented 5 years ago

Some thoughts I want to jot down right now:

These may not necessarily have much to do with the PR, and certainly don't have to be flushed out to merge. This is just for my own sake in thinking about about clever ways to operate around behave. So, please feel free to ignore the following....

Potentially useful transformers to provide

Implementing as a matcher?

I wonder if this can somehow be provided with a custom matcher/Match class in such a way that all definitions could gain this benefit effortlessly.

Background info:

A matcher has a match method which returns a Match object with arguments returned from the matcher's check_match method.

The Match object provides a run method which calls the decorated function with the arguments. But what's important to note is this method has access to the context (where matchers do not).

So, I am plotting in my mind if it's possible to write some custom matchers and Match object that accomplishes this thought. Since behave-webdriver utilizes two kinds of matchers, we'd need this for both the parse matcher and regex matcher.

(aside: Unfortunately, because of some inflexibilities with behave I don't think an end-user could easily provide this themselves. So we have to make up for this inflexibility.)

A possible mockup

from behave.matchers import Match, ParseMatcher, RegexMatcher
from behave_webdriver.parameter_transformations import NoTransformation

class TransformingMatch(Match):
    def run(self, context):
        args = []
        kwargs = {}
        for arg in self.arguments:
            if arg.name is not None:
                kwargs[arg.name] = arg.value
            else:
                args.append(arg.value)

        with context.use_with_user_mode():
            #  the above is a COPY/PASTE of the original `run` implementation,
            # THESE next 3 lines are the key change vvv
            transformer = context.get('transformer', NoTransformation())
            args = [transformer.eval(arg) for arg in args]
            kwargs = {name: transformer.eval(value) for name, value in kwargs.items()}
            self.func(context, *args, **kwargs)

class TransformMixin(object):
    def match(self, step):
        # -- PROTECT AGAINST: Type conversion errors (with ParseMatcher).
        try:
            result = self.check_match(step)
        except Exception as e:  # pylint: disable=broad-except
            return MatchWithError(self.func, e)

        if result is None:
            return None     # -- NO-MATCH
        #  the above is a COPY/PASTE of original implementation
        #  THIS one line is the key v
        return TransformingMatch(self.func, result)

class TransformParseMatcher(TransformMixin, ParseMatcher):
    pass

class TransformRegexMatcher(TransformMixin, RegexMatcher):
    pass

Now, suppose that behave-webdriver registers these matchers and uses them........ I believe that's all we would need to give all definitions the benefit of transformations automagically based on a transformer set on context.transformer (or some other aptly named attribute)

Then we just change the lines where we call use_step_matcher and replace with the two matchers defined above.

/endrambling

urosn74 commented 5 years ago

Hi @spyoungtech. Thanks for taking the time for this PR. If I understand you correctly, matchers would "magically" apply transformations on parameters, before the call to the step function? That would be quite powerful and would keep step implementations nice and tidy.

Environment variables were the original "target", though I think it is already quite easy to pull them into the context that is used for parameter transformations. Unless you aim to eliminate the need to initialise the context in the environment - then it would make sense to move that concern to a specialised transformer.

spyoungtech commented 5 years ago

matchers would "magically" apply transformations on parameters, before the call to the step function

That's the idea. It's actually the custom Match object that handles this, since that's what will ultimately have access to the context, where a user could supply a 'transformer'. We alter the matchers to make sure they use the custom TransformingMatch class, instead of the regular Match.

I've merged this into a feature branch for further work. I've opened #61 to continue tracking this effort.

spyoungtech commented 5 years ago

63 releases this change :)