Python

Understanding Fixtures in PyTest

1. Introduction

PyTest is used for developing automation tests using python. It is a very powerful framework that can be utilized to write effective test automation scenarios in python. PyTest framework makes it easy to write small tests, yet scalable, to support complex applications and libraries.

2. PyTest fixtures

The purpose of test fixtures is to provide an inbuilt baseline which would provide repeated and reliable execution of tests. Fixtures help in reducing time and effort of implementing a function several times. Instead of implementing and defining a function, which would be used repeatedly, just call the same function as a fixture object and get it executed.

3. Passing fixture objects as function arguments

Test functions can receive fixture objects by invoking them as input arguments. To define a fixture function we have to use the decorator @pytest.fixture .Let us consider a very small example to understand how a fixture can be implemented in real applications.

Using fixture to pass function as an object

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import WebDriverException, NoSuchElementException
import traceback, time

@pytest.fixture
def selenium_driver():
  try:
    driver = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver')
    print("\n>>>Driver returned")
    return driver
  except WebDriverException:
    print("\n>>>WebDriver configuration error")
    traceback.print_exc()

def test_function(selenium_driver):
  try:
    #Navigate to google
    driver.get("https://www.google.co.in/")
    #Maximize browser window
    browser.maximize_window()
    browser.implicitly_wait(5)
    #Enter keyword in input panel
    browser.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys("Some input")
    print("\n>>>Entered data to search")
    #Press enter to search
    browser.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys(Keys.ENTER)
    print("\n>>Key pressed to search element")
    time.sleep(5)
    browser.find_element(By.XPATH,"//a[contains(text(),'Some text')]").click()
    #Validate the title of the page
    assert browser.title == "Some title"
    print("\n>>>Title of the page verified")
  except NoSuchElementException:
    print("\n>>>Element not found")
    traceback.print_exc()

4. Fixtures: An example of dependency injection

Using fixture can be considered as a strong example for dependency injection. Fixtures allow a test function to operate by calling an already pre-initialized application object without caring much about the import/clean up/set up details. The fixture functions act as a dependency injector and the test functions which utilize the fixture object are the consumers.

5. Using fixtures across tests in a module (class/sessions)

Fixtures can be declared to be used within modules, class or a particular session. To accomplish this, the scope of a fixture has to be defined, which means, during the declaration of a fixture we have to define the scope as module/session/class. For example:

Using fixture to pass function as an object to modules

#contents of selenium_driver.py
from selenium import webdriver
from selenium.common.exceptions import WebDriverException
import traceback

@pytest.fixture(scope="module")
def selenium_driver():
  try:
    driver = webdriver.Chrome(executable_path='/usr/local/bin/chromedriver')
    print("\n>>>Driver returned")
    return driver
  except WebDriverException:
    print("\n>>>WebDriver configuration error")
    traceback.print_exc()

#contents of test1.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
import traceback, time
def test1(selenium_driver):
  try:
    #Navigate to google 
    driver.get("https://www.google.co.in/") 
    #Maximize browser window browser.maximize_window() 
    driver.implicitly_wait(5) 
    #Enter keyword in input panel 
    driver.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys("Some input") 
    print("\n>>>Entered data to search") 
    #Press enter to search 
    driver.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys(Keys.ENTER) 
    print("\n>>Key pressed to search element") 
    time.sleep(5) 
    driver.find_element(By.XPATH,"//a[contains(text(),'Some text')]").click() 
    #Validate the title of the page 
    assert browser.title == "Some title" 
    print("\n>>>Title of the page verified") 
  except NoSuchElementException: 
   print("\n>>>Element not found") 
   traceback.print_exc()

#contents of test2.py
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.common.exceptions import NoSuchElementException
import traceback, time
def test2(selenium_driver):
  try:
    #Navigate to google
    driver.get("https://www.google.co.in/")
    #Maximize browser window
    driver.maximize_window()
    driver.implicitly_wait(5)
    #Enter keyword in input panel
    driver.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys("Some input")
    print("\n>>>Entered data to search")
    #Press enter to search
    driver.find_element(By.CSS_SELECTOR,"input[name='q']").send_keys(Keys.ENTER)
    print("\n>>Key pressed to search element")
    time.sleep(5)
    driver.find_element(By.XPATH,"//a[contains(text(),'Some text')]").click()
    #Validate the title of the page
    assert browser.title == "Some title"
    print("\n>>>Title of the page verified")
  except NoSuchElementException:
    print("\n>>>Element not found")
    traceback.print_exc()

6. Yield

The object yield in a fixture is used to execute setup or tear down code. If you have any piece of code that needs to be executed in a sequence of set up and tear down then you can use the decorator @pytest.yield_fixture. For example, if I want to instantiate a driver as well as terminate the browser session using the same block of code then you can use yield to achieve this.

Using yield to setup/tear down

import pytest
from selenium import webdriver

@pytest.yield_fixture(scope="function")
def SetUp(request, browser):
    print("\nRunning one time setUp")
    wdf = WebDriverFactory(browser)
    driver = wdf.getWebDriverInstance()

    if request.cls is not None:
        request.cls.driver = driver

    yield driver
    driver.quit()
    print("\nRunning one time tearDown")

When pytest runs the above function it will look for a fixture SetUp and run it. Whatever is yielded (or returned) will be passed to the corresponding test function. The “scope” of the fixture is set to “function” so as soon as the test is complete, the block after the yield statement will run.

7. Conclusion

I have only touched on a particular, yet powerful feature of PyTest. I would highly recommend you to go through the framework to understand how PyTest functions work and to get started on automating test scenarios using PyTest.

Soumyajit Basu

Soumyajit is a QA/DevOps engineer by profession and a technology enthusiast by passion. He loves communicating about technology and is an author at his own blog platform as well as in Dzone and Web Code Geeks.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button