This blog will teach us to build a BDD web UI test automation suite using Pytest-BDD. Pytest-BDD is a test framework for enabling behavior-driven tests in Python. Unlike many other BDD tools, with Pytest-BDD, we do not need a separate runner file; it also inherits the power and flexibility of Pytest. But before getting started with Pytest-BDD, let us look at what BDD is and why we need it.
Behavior in software describes how a feature responds or behaves when the user performs a scenario. It captures a set of inputs, performed actions, and expected outcomes. Behavior-driven development (BDD) is a methodology for developing software or tests where the behavior of the software is put first and then the development is done accordingly.
As we know selenium supports a set of programming languages but here are the reasons to use Python over any other programming language with Selenium.
The current supported Python versions are 3.5 and above.
Selenium is a free (open source) automated testing framework used to validate web applications across different browsers and platforms.
For selenium installation, you can use the pip command.
pip install selenium
Pytest is a mature and extensive Python testing framework we can use with Selenium to write our automated tests. Using Pytest, we can also define the code we need to run before and after the test execution just like TestNG.
We can install Pytest using the pip command.
pip install -U pytest
Pytest-BDD is a plugin for pytest, which is one of the best test frameworks in Python, and also one of the most popular. Pytest makes it very easy to write simple but powerful tests. It also comes with a bunch of other plugins such as code coverage, HTML reports, or parallel execution and all the pytest plugins are fully compatible with pytest-bdd. We can use conftest fixtures similar to pytest to manage context between functions.
We install pytest-bdd using the following pip command.
pip install pytest-bdd
Similar to the convention of pytest, our test files should be named as test_*.py or *_test.py files and all the test items in those files should use
But, in this case, our test files will be our step definition files (representatives) of our feature files in the BDD framework. Those files will contain the test code of our gherkin feature files.
Project
|_ Pages
|_ *page.py
|_ Test
|_ features
*.feature
|_ steps
|_ test_step_defs.py
Referring to the folder hierarchy above, we have created a directory named features that will hold our feature files and a directory for step definition files named steps. We will provide the path for a feature file in a scenario defined in a step definition file. Other names and hierarchies may be used. For example, large test suites can have feature-specific directories of features and step defs. pytest should be able to discover tests anywhere under the test directory
The step statements highlighted by IDE show that no step definition is available for this gherkin.
1) Right-click on the step statement.
2) Click on Show Context Actions.
3) Click on Create all step definitions if you want to create all step definition methods at once. Otherwise, you can also create a single-step definition method with ‘Create step definition’
4) Click on Create a new file
A Python file will be created at the specified location with all the step definition methods.
from pytest_bdd import scenario, given, when, then
@given("I am on the angus media login page")
def step_impl():
raise NotImplementedError(u'STEP: Given I am on the angus media login page')
@when("I enter <username> and <password>")
def step_impl():
raise NotImplementedError(u'STEP: When I enter <username> and <password>')
@given("I click on the login button")
def step_impl():
raise NotImplementedError(u'STEP: And I click on the login button')
@then("I should be successfully logged in")
def step_impl():
raise NotImplementedError(u'STEP: Then I should be successfully logged in'
5. Implement the logic for each step
@given("I am on the angus media login page")
def navigate_to_login_page(browser):
try:
HomePage(browser).navigate_to_login()
assert LoginPage(browser).check_login_button_enabled() == True
except:
raise Exception('Navigation to login failed')
“browser” passed in each step definition method is the fixture defined in the contest file.
Note: In this case, we do not need to use the annotation pytest.mark.fixture as we use in pytest.
@pytest.fixture
def browser():
opts = webdriver.ChromeOptions()
opts.add_argument('start-maximized')
opts.add_argument('headless')
b = webdriver.Chrome(ChromeDriverManager().install(), options=opts)
yield b
b.close()
b.quit()
@scenario('../login.feature', 'Login with RO valid credentials')
def test_login():
pass
@when(parsers.parse('I enter {username} and {password}'))
def enter_username_and_password(browser, username, password):
try:
base64_bytes = password.encode("ascii")
string_bytes = base64.b64decode(base64_bytes)
n_password = string_bytes.decode("ascii")
LoginPage(browser).enter_credentials(username, n_password)
except:
raise Exception('Entering credentials failed')
If you execute the command pytest in the step directory, It will invoke all of the test files. For running a single test file and all the test methods it contains, we use this command.
And for running a specific test function, we do it this way
The test result and execution time will be shown like this in the terminal.
Variables in Gherkin can be parametrized in step definition methods in the following way.
@when(parsers.parse('I enter {username} and {password}'))
def enter_username_and_password(browser, username, password):
try:
base64_bytes = password.encode("ascii")
string_bytes = base64.b64decode(base64_bytes)
n_password = string_bytes.decode("ascii")
LoginPage(browser).enter_credentials(username, n_password)
except:
raise Exception('Entering credentials failed')
username and password written in curly brackets are passed as parameters in the step definition method and the values will be picked by the table defined in the example of the scenario outline in the feature file.
@login
Feature: LogIn
As a test automation engineer,
I want to login into the angus media application,
So that I can verify the login functionality
Background: Angus Media Home
Given I am on the angus media home page
@smoke @sanity @UI @positive @web
Scenario Outline: Login with RO valid credentials
Given I am on the angus media login page
When I enter <username> and <password>
And I click on the login button
Then I should be successfully logged in
Examples:
| username | password |
| [email protected] | password_1 |
| [email protected] | password_2 |
We can define a common precondition for scenarios as a given step in the Background. This shared step can be defined in a conftest file and will be executed before each test scenario.
Tagging in BDD is a way to categorize your scenarios. We can also provide a tag to a feature and this way all the scenarios in a feature will be automatically tagged with the given tag.
In Pytest-BDD, we can invoke our tests by passing tags in the pytest command and only those scenarios related to the given tag will be executed.
We provided a tag @signup to the feature. All the scenarios in the feature will be executed with the following command.
To avoid the pytest warning, we also need to define our tags in the pytest.ini file stored in the root directory of the project.
[pytest]
markers =
smoke: for running smoke test cases
sanity: for running sanity test cases
regression: for running test cases in the regression suite
positive: for running only happy scenarios
negative: for running negative scenarios
UI: for test cases related to web UI
book_creation_cd: for custom design book creations
home: for test cases related to the home page
login: for test cases related to the login page
signup: for signup scenarios
serial
Thanks for reading, Don’t forget to share your thoughts in the comment section!
USA408 365 4638
1301 Shoreway Road, Suite 160,
Belmont, CA 94002
Whether you are a large enterprise looking to augment your teams with experts resources or an SME looking to scale your business or a startup looking to build something.
We are your digital growth partner.
Tel:
+1 408 365 4638
Support:
+1 (408) 512 1812
COMMENTS ()
Tweet