One of the greatest strengths of Vedro is its scenario-based approach to testing. By modeling tests as clear-cut user stories, Vedro helps you write behavior-driven tests that reflect real-world usage. However, while scenario classes are fantastic for complex tests (like end-to-end scenarios), they can sometimes feel verbose when writing smaller, more straightforward checks, such as unit tests.
Why Scenario Classes Sometimes Feel Heavy
Here is a simple example of a Vedro scenario class decoding a base64-encoded string:
<span>import</span> <span>base64</span><span>import</span> <span>vedro</span><span>class</span> <span>Scenario</span><span>(</span><span>vedro</span><span>.</span><span>Scenario</span><span>):</span><span>subject</span> <span>=</span> <span>"</span><span>decode base64 encoded string</span><span>"</span><span>def</span> <span>given</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span><span>def</span> <span>when</span><span>(</span><span>self</span><span>):</span><span>self</span><span>.</span><span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>self</span><span>.</span><span>encoded</span><span>)</span><span>def</span> <span>then</span><span>(</span><span>self</span><span>):</span><span>assert</span> <span>self</span><span>.</span><span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span><span>import</span> <span>base64</span> <span>import</span> <span>vedro</span> <span>class</span> <span>Scenario</span><span>(</span><span>vedro</span><span>.</span><span>Scenario</span><span>):</span> <span>subject</span> <span>=</span> <span>"</span><span>decode base64 encoded string</span><span>"</span> <span>def</span> <span>given</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span> <span>def</span> <span>when</span><span>(</span><span>self</span><span>):</span> <span>self</span><span>.</span><span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>self</span><span>.</span><span>encoded</span><span>)</span> <span>def</span> <span>then</span><span>(</span><span>self</span><span>):</span> <span>assert</span> <span>self</span><span>.</span><span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span>import base64 import vedro class Scenario(vedro.Scenario): subject = "decode base64 encoded string" def given(self): self.encoded = "YmFuYW5h" def when(self): self.decoded = base64.b64decode(self.encoded) def then(self): assert self.decoded == b"banana"
Enter fullscreen mode Exit fullscreen mode
It’s certainly clear and descriptive, but for a small test like this, the class-based approach with separate methods can be a bit more than you need. This led us to develop vedro-fn: a plugin offering a concise, function-based syntax.
Introducing vedro-fn
With vedro-fn
, you can write the same test in a simpler, more streamlined style:
<span>import</span> <span>base64</span><span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>given</span><span>,</span> <span>when</span><span>,</span> <span>then</span><span>@scenario</span><span>()</span><span>def</span> <span>decode_base64_encoded_str</span><span>():</span><span>with</span> <span>given</span><span>:</span><span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span><span>with</span> <span>when</span><span>:</span><span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span><span>with</span> <span>then</span><span>:</span><span>assert</span> <span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span><span>import</span> <span>base64</span> <span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>given</span><span>,</span> <span>when</span><span>,</span> <span>then</span> <span>@scenario</span><span>()</span> <span>def</span> <span>decode_base64_encoded_str</span><span>():</span> <span>with</span> <span>given</span><span>:</span> <span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span> <span>with</span> <span>when</span><span>:</span> <span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span> <span>with</span> <span>then</span><span>:</span> <span>assert</span> <span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span>import base64 from vedro_fn import scenario, given, when, then @scenario() def decode_base64_encoded_str(): with given: encoded = "YmFuYW5h" with when: decoded = base64.b64decode(encoded) with then: assert decoded == b"banana"
Enter fullscreen mode Exit fullscreen mode
Notice how the Scenario class has been replaced with a single decorated function. The steps (given
, when
, then
) are represented by context blocks rather than class methods. This is particularly convenient for tests that don’t need a full scenario class structure.
Key points:
- Just add
@scenario()
to any function. - Inside the function, use
with given
,with when
, andwith then
blocks to define your test flow. - Everything else works as you’d expect in Vedro: reporting, command line usage, test discovery, etc.
Installation
Just run:
<span>$ </span>vedro plugin <span>install </span>vedro-fn<span>$ </span>vedro plugin <span>install </span>vedro-fn$ vedro plugin install vedro-fn
Enter fullscreen mode Exit fullscreen mode
And that’s it! You can now import and use the @scenario
, given
, when
, and then
blocks as shown above.
Using Additional Vedro Features
Reusing Scenario Decorators
In standard Vedro, you can decorate your scenario classes with @skip
, @only
, and other scenario-level decorators. With vedro-fn
, you can reuse these decorators by including them in the subscript of @scenario
, as shown below:
<span>import</span> <span>base64</span><span>from</span> <span>vedro</span> <span>import</span> <span>skip</span><span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>given</span><span>,</span> <span>when</span><span>,</span> <span>then</span><span>@scenario</span><span>[</span><span>skip</span><span>]()</span><span>def</span> <span>decode_base64_encoded_str</span><span>():</span><span>with</span> <span>given</span><span>:</span><span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span><span>with</span> <span>when</span><span>:</span><span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span><span>with</span> <span>then</span><span>:</span><span>assert</span> <span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span><span>import</span> <span>base64</span> <span>from</span> <span>vedro</span> <span>import</span> <span>skip</span> <span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>given</span><span>,</span> <span>when</span><span>,</span> <span>then</span> <span>@scenario</span><span>[</span><span>skip</span><span>]()</span> <span>def</span> <span>decode_base64_encoded_str</span><span>():</span> <span>with</span> <span>given</span><span>:</span> <span>encoded</span> <span>=</span> <span>"</span><span>YmFuYW5h</span><span>"</span> <span>with</span> <span>when</span><span>:</span> <span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span> <span>with</span> <span>then</span><span>:</span> <span>assert</span> <span>decoded</span> <span>==</span> <span>b</span><span>"</span><span>banana</span><span>"</span>import base64 from vedro import skip from vedro_fn import scenario, given, when, then @scenario[skip]() def decode_base64_encoded_str(): with given: encoded = "YmFuYW5h" with when: decoded = base64.b64decode(encoded) with then: assert decoded == b"banana"
Enter fullscreen mode Exit fullscreen mode
This function is now skipped by the test runner, exactly like a skipped scenario class.
Parametrizing Your Tests
Need to test a variety of inputs and expected outputs? Parametrization works almost the same way it does in standard Vedro — just pass a list of params
to the @scenario()
decorator:
<span>import</span> <span>base64</span><span>from</span> <span>vedro</span> <span>import</span> <span>params</span><span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>when</span><span>,</span> <span>then</span><span>@scenario</span><span>([</span><span>params</span><span>(</span><span>"</span><span>YmFuYW5h</span><span>"</span><span>,</span> <span>b</span><span>"</span><span>banana</span><span>"</span><span>),</span><span>params</span><span>(</span><span>""</span><span>,</span> <span>b</span><span>""</span><span>),</span><span>])</span><span>def</span> <span>decode_base64_encoded_str</span><span>(</span><span>encoded</span><span>,</span> <span>expected</span><span>):</span><span>with</span> <span>when</span><span>:</span><span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span><span>with</span> <span>then</span><span>:</span><span>assert</span> <span>decoded</span> <span>==</span> <span>expected</span><span>import</span> <span>base64</span> <span>from</span> <span>vedro</span> <span>import</span> <span>params</span> <span>from</span> <span>vedro_fn</span> <span>import</span> <span>scenario</span><span>,</span> <span>when</span><span>,</span> <span>then</span> <span>@scenario</span><span>([</span> <span>params</span><span>(</span><span>"</span><span>YmFuYW5h</span><span>"</span><span>,</span> <span>b</span><span>"</span><span>banana</span><span>"</span><span>),</span> <span>params</span><span>(</span><span>""</span><span>,</span> <span>b</span><span>""</span><span>),</span> <span>])</span> <span>def</span> <span>decode_base64_encoded_str</span><span>(</span><span>encoded</span><span>,</span> <span>expected</span><span>):</span> <span>with</span> <span>when</span><span>:</span> <span>decoded</span> <span>=</span> <span>base64</span><span>.</span><span>b64decode</span><span>(</span><span>encoded</span><span>)</span> <span>with</span> <span>then</span><span>:</span> <span>assert</span> <span>decoded</span> <span>==</span> <span>expected</span>import base64 from vedro import params from vedro_fn import scenario, when, then @scenario([ params("YmFuYW5h", b"banana"), params("", b""), ]) def decode_base64_encoded_str(encoded, expected): with when: decoded = base64.b64decode(encoded) with then: assert decoded == expected
Enter fullscreen mode Exit fullscreen mode
This will generate separate test cases for each set of parameters passed in.
Current Limitations
While vedro-fn
provides nearly all the features you know and love from Vedro, there are a couple of considerations:
- No Scope Support: Scope-related functionality is not supported because there is no underlying scenario instance to attach them to.
- No Step Function Decorators: Since steps (
given
,when
,then
) are not individual methods invedro-fn
but rather code blocks within a function, you cannot decorate them separately.
Despite these limitations, everything else — various reporters, parallel execution, external plugins — works seamlessly with vedro-fn
.
Share Your Feedback!
vedro-fn
is an experimental plugin. If you find it helpful for tidying up smaller tests or want to see more functionality added, let us know! Your feedback will determine whether vedro-fn
becomes a fully supported part of the Vedro core.
暂无评论内容