Now that we know about behavior-driven development, let's start using
We'll start by looking at
pytest, the foundation upon which
pytest-bdd is built, and then we'll set up a project together that uses
pytest is a fantastic, free and open-source test framework in Python.
According to the 2018 Python developer survey, it's also the most popular Python test framework by far. What makes
pytest so great is the fact that it's so simple and concise and Pythonic.
Here on pytest.org's homepage, we can see an example of a simple test. Here we have a simple function that does an increment operation. The test case is a one-line function, prefixed with “test_”, that does the basic
assert. We can see, based on results, it's totally functional.
This is why people love pytest. It's so easy to get it up and running and start writing meaningful effective test cases.
One thing that makes pytest stand out from other test frameworks is its fixtures.
Fixtures are essentially functions that do setup and cleanup operations in a much more scalable and reusable way.
Let's take a look at the example fixture function here on pytest.org's documentation page.
# content of ./test_smtpsimple.py import pytest @pytest.fixture def smtp_connection(): import smtplib return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert 0 # for demo purposes
This is what a fixture looks like right here.
It's prefixed by the decorator
@pytest.fixture and what it does is it sets something up, in this case an SMTP connection. All it does is it sets it up, and then it returns that object.
Any test case function can use a fixture by declaring it as a function argument.
What this does is it will run the fixture and do dependency injection with that returned reference, so that the test case has access to that object, as we can see here.
Fixtures can also be controlled by scope, whether they should be run once per test case, or maybe once per the entire tests we run.
I'm bringing up fixtures because this will become important later when we talk about how
pytest-bdd handles things with its step definitions.
Another awesome facet of the pytest framework is its ability to have extra plugins.
Plugins are a way to extend pytest, usually with some sort of extra bindings or extra features, or whatever you need to do to make integrations with other sorts of frameworks. This makes pytest essentially a limitless framework, because anyone can add plugins.
Pytest provides a bunch of standard ones, as listed here, such as integrations for Django and Flask, or maybe code coverage, or xdist which will run your tests in parallel.
We can even see on the list here the one that we're interested in,
So how does
pytest-bdd fit in with
As we saw on the previous page, pytest-bdd is a plugin for
pytest. What that means is that it adds all that extra goodness of BDD on top of the core
If we look at the documentation page for
pytest-bdd on GitHub, we can see that that goodness is basically Gherkin feature files, together with some Python functions and decorators for writing step definitions that will execute those Gherkin steps.
pytest-bdd test cases are essentially run interchangeably as pytest tests.
That's why I favored using
pytest-bdd above other BDD frameworks in Python. The fact that
pytest-bdd is a plugin for pytest means that it gets to leverage all the goodness of pytest, including the fixtures and other plugins.
It's not standalone or isolated, so
pytest-bdd as a project can focus on what it's good at and rely on other plugins for other features to make testing even better.
The rest of this course will focus exclusively on the
If you want to learn more about pytest, I strongly recommend these 2 books: Python Testing With pytest by Brian Okken, as well as pytest Quick Start Guide by Bruno Oliveira. I've personally read both of these books and can attest that they are excellent resources for learning all the ins and outs of pytest.
So now that we know about
pytest-bdd, let's set up a project together.
As a reminder, all of the code in this test automation university course is also available on my GitHub account, under the tau-pytest-bdd repository. I'll create a branch for each chapter so that you can follow along with the example code as we go.
Let's set up that project together.
I'm already at my terminal, and as you can see, I've already cloned my GitHub repository.
Every time I start working on a new project in Python, I like to create a virtual environment for that project to keep all of my dependencies well controlled. My preferred tool for that is
So, I'm going to set up my
pipenv for my project, using
Boom. We're ready to go.
Now that I have
pipenv installed, I'm going to use
pipenv to install 2 dependency packages:
pipenv install pytest, as well as
pipenv install pytest-bdd, which is the plugin.
Also, worth noting, I'm using Python 3.7, which is one of the more recent versions of Python. Once all this is set up, in my project I'll have a
Pipfile and a
Pipfile.lock which will have all of my dependencies.
We should be good to go.
Now that we have our project set up, the next question naturally is, what IDE or editor should I use for developing my project?
Personally, I recommend PyCharm. According to that 2018 developer survey I mentioned before, PyCharm is one of the most popular Python IDEs. And for good reason. It's really nice to use, it has a lot of features.
Note about Pycharm
Warning: PyCharm Community Edition does NOT support BDD frameworks. If you get the Professional edition, it also supports many behavior-driven development frameworks, including ours,
I'll be using PyCharm throughout our course as my IDE, but feel free to use whichever one you like best.
In our next lesson, we'll be writing our first test case using