Python

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.

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 of 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:

#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 teardown 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.

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.

Published on Web Code Geeks with permission by Soumyajit Basu, partner at our WCG program. See the original article here: Fixtures in PyTest

Opinions expressed by Web Code Geeks contributors are their own.

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
Inline Feedbacks
View all comments
Back to top button