Web UI Test Automation with Pytest-BDD

Web UI Test Automation with Pytest-BDD
COMMENTS (0)
Tweet

Introduction

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.

BDD

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.

Why should we use BDD?

  • Separating individual behaviors of software helps us understand the system without any complications
  • Overcomes the communication gap between technical and non-technical teams
  • Improves planning
  • Comprehensive development for automated tests
  • Improves documentation
  • Makes the framework more scalable
  • Reusability

Prerequisites

 

  1. Python

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.

  • Comparatively, python minimizes the time to run the script, furthermore curtailing the time of execution
  • The code flow of Python is understandable as it uses indentation instead of braces in the initiation and end blocks.

The current supported Python versions are 3.5 and above.

  1. Selenium 

Selenium is a free (open source) automated testing framework used to validate web applications across different browsers and platforms.

  • Selenium supports a set of programming languages to work with.
  • It also supports a set of different browsers though cross-browser testing can be easily achieved.
  • It supports parallel execution.
  • Provides easy integration with CI/CD and reporting tools.
  • It has a mature online community.

For selenium installation, you can use the pip command.

pip install selenium

 

  1. Pytest

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.

  • Pytest has a way of auto-discovering test modules and functions.
  • It provides detailed info on the assert statements.
  • Supports parallel execution
  • Provides support for defining the order of test execution, skipping a test, or executing a subset of a test suite

We can install Pytest using the pip command.

pip install -U pytest

Pytest-BDD

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.

Installation

We install pytest-bdd using the following pip command.

pip install pytest-bdd

Conventions

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

  • Test prefixed test functions or methods outside of class-test prefixed test functions or methods inside
  • Test prefixed test classes (without an __init__ method)

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

Writing a Test

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

 

Creating a feature file

  1. Right-click on the desired directory and create a new Gherkin feature file.

  1. Write your Gherkin scenario(s) in the feature file.

The step statements highlighted by IDE show that no step definition is available for this gherkin.

 

Create a Step Definition File 

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()
  1. Write a test method as suggested by pytest in the step definition file to execute the test. Specify the feature file path we need to refer to and the scenario we want to execute.
@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')

Invoking the tests 

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.

Parameterization 

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  |

Shared Steps

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 

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!

Safa Amjad is working as a Test Automation Engineer in Folio3 with the total experience of more than 2.5 years. She specializes in web UI test automation in Selenium with Python with a strong background in Selenium, Java and TestNG. She is passionate to deliver efficient and reliable automated testing solutions.

CALL

USA408 365 4638

VISIT

1301 Shoreway Road, Suite 160,

Belmont, CA 94002

Contact us

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