python-3.xselenium-webdriverpytestbddpytest-bdd

Group Page class objects in my step-definition.py for pytest-bdd


I am a beginner to BDD testing using pytest-bdd framework. I am trying to build a framework using Page object model technique. I have created below page class :


login.py

from selenium import webdriver
import time

class Login():
    def __init__(self,driver):
        self.driver = driver


    def click_login(self):
        element = self.driver.find_element_by_link_text('Login')
        element.click()

    def username_available(self):
        self.driver.find_element_by_id("user_email").send_keys("test@test.com")
        self.driver.find_element_by_id("user_password").send_keys("xyzxyz")
        time.sleep(5)
        self.driver.find_element_by_xpath("//input[@value='Log In']").click()

Below is my feature file.

Feature: LetsKodeIt Website
  As an IT engineer,
  I want to find information online
  So I can learn new things


  Scenario: LetsKodeIt Login One
    Given the letskodeit website is opened
    When I seen login
    And I click on login
    Then login page with username and password is displayed



  Scenario: LetsKodeIt Login Two
    Given the letskodeit website is opened
    When I seen login
    And I click on login
    Then login page with username and password is displayed

conftest.py

import pytest
from selenium import webdriver
from pytest_bdd import given

@pytest.fixture()
def setup():
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.implicitly_wait(10)

    yield driver
    driver.quit()


@given("the letskodeit website is opened")
def browser(setup):
    setup.get("https://letskodeit.teachable.com/")

test_google_search.py

import pytest
from pytest_bdd import scenario,given,when,then
from selenium import webdriver
from pages.login_page import Login



@scenario("C:\\Users\\A610037\\PycharmProjects\\pytest-bdd\\tests\\features\\letskodeit.feature","LetsKodeIt Login One")
def test_one():
    pass

@when("I seen login")
@when("I click on login")
def step_imp(setup):
    log = Login(setup)
    log.click_login()

@then("login page with username and password is displayed")
def step_imp(setup):
    log = Login(setup)
    log.username_available()


@scenario("C:\\Users\\A610037\\PycharmProjects\\pytest-bdd\\tests\\features\\letskodeit.feature","LetsKodeIt Login Two")
def test_two():
    pass

@when("I seen login")
@when("I click on login")
def step_imp(setup):
    log = Login(setup)
    log.click_login()

@then("login page with username and password is displayed")
def step_imp(setup):
    log = Login(setup)
    log.username_available()

Every time I add any step definition in my test.py file, I have to create an object of my login page class. Is it possible to create the page object only once and then call that object in my @when,@then step definitions? Below is my project structure project-hierarchy


Solution

  • The following solutions come to my mind:

    1. Make the Login Page a pytest fixture and use it like the setup fixture in your step definitions
    2. Use an extra given step in your feature file and add target_fixture="login_page" to it -> pytest-bdd will create a pytest fixture with name login_page, that can be used like the setup fixture in your step definitions
    3. Use a context fixture (e.g. a dictionary) which you include in all your step definitions and where you store the login page object in, when creating it the first time

    Use pytest fixture

    
    @pytest.fixture
    def login_page(setup):
        return Login(setup)
    
    @when("I seen login")
    @when("I click on login")
    def click_on_login(login_page):
        login_page.click_login()
    

    Use given step for fixture creation

    @given("a login page", target_fixture="login_page")
    def login_page(setup):
        return Login(setup)
    
    @when("I seen login")
    @when("I click on login")
    def click_on_login(login_page):
        login_page.click_login()
    

    Use a context object

    @pytest.fixture
    def step_context(setup):
        return {'login_page': Login(setup)}
    
    @when("I seen login")
    def login_seen(setup, step_context):
        step_context['login_page'] = Login(setup)
    
    @when("I click on login")
    def click_on_login(step_context):
        step_context['login_page'].click_login()