If you've been running
I've worked on Python test automation projects long enough to know some tips and tricks for the best results. I'll share seven tips and practices you can depend on for efficient Python test automation.
Python comes with its built-in test automation framework, Unittest. But it's best for small projects. If you want a test automation framework that allows you to work on small—to large-scale projects, Pytest is a better option.
Pytest provides a structured and efficient way to write test cases for more manageable and scalable test automation. It also has the simplest syntax and the most comprehensive plugin ecosystem. For example, you can use pytest-xdist for parallel test execution. In addition, you can run test cases you've written in Unittest or Nose 2 on Pytest.
If you do more robot process automation (
If you work on a team that practices Behavior-Driven Development (BDD), Behave is the best framework. Writing test cases in Behave that non-technical stakeholders can understand is super easy.
I'd advise you to choose a framework that matches the tech stack your team uses to avoid any communication barriers. But if you're working solo, Pytest is the most versatile and commonly used framework.
I mentioned that you can integrate Pytest with Selenium for web testing. While Pytest is great for functional testing, Selenium takes web automation to a whole new level. Selenium has powerful browser automation capabilities, especially with its WebDriver library.
When you run tests on Selenium, you have to wait some defined amount of time as the system works on locating elements. Once that time elapses, say 10 seconds, the driver quits. The issue with implicit wait, as it's called, is that different elements need different wait times.
So, instead of relying on implicit waits, use WebDriverWait in conjunction with expected_conditions to create certain conditions that must be met before elements become interactive.
from selenium import webdriver
from Selenium.webdriver.common.by import By
from Selenium.webdriver.support.ui import WebDriverWait
from Selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get("https://example.com")
# Wait for the element to be clickable
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID, "clickableElementId")))
element.click() # Perform action after the element is ready
driver.quit()
If your tests constantly fail, it may not necessarily mean the web app has any issues. It could simply be because of some changes in the app's code or UI. To avoid excessive debugging from test failures, ensure you use unique IDs to identify web elements accurately.
I can't stress enough how time-saving Pytest parameterized testing features are. Test automation sometimes requires testing the same functionality in an app with different sets of data. Instead of duplicating the test functions, you can simply run a single test function using pytest.mark.parametrize
, and it'll be duplicated multiple times with different parameters. Here's what that looks like:
import pytest
@pytest.mark.parametrize("input, expected", [(2, 4), (3, 9), (4, 16)])
def test_square(input, expected):
assert input ** 2 == expected
If you're wondering if you can run parameterized testing with Python's built-in test automation framework, the answer is yes—under certain conditions. Unittest does not natively support parameterized testing, but you can add a module for that purpose. Here's how to do that:
import unittest
from parameterized import parameterized
class TestMathOperations(unittest.TestCase):
@parameterized.expand([
("square of 2", 2, 4),
("square of 3", 3, 9),
("square of 4", 4, 16),
])
def test_square(self, name, input, expected):
self.assertEqual(input ** 2, expected)
The more APIs, test cases, databases, and third-party systems in your testing suite, the more execution lags, which can slow down development. Thankfully, there's a way to deal with that without tampering with your test suite. Python allows for parallel testing with frameworks like Pytest. You can run several tests in parallel using multiple CPUs.
Here's how to execute parallel testing with Pytest:
pip install pytest-xdist
pytest -n 4 # Run tests on 4 CPUs
You can also increase the number of CPUs handling the load, but there's only so much local infrastructure
Trust me; you won't catch a break if you're working with fast-paced development cycles, and you have to manually adjust code every time something changes in software.
You can automate Python tests into your CI/CD pipeline to execute them automatically whenever the code changes. This allows you to quickly identify bugs and release the improved app version back for use.
To integrate Python testing automation into your CI/CD pipelines. Add pytest-cov
to your project and use a Pytest command to automatically execute your tests and flag any issues.
Steps:
- name: Install dependencies
run: pip install pytest pytest-cov
- name: Run tests
run: pytest --cov=my_project
If you work on larger test suites, you'd have to add a robot framework to CI tools like Jenkins. For Unittest, coverage allows you to integrate your test into a pipeline like this;
Test:
script:
- Python -m coverage run -m unittest discover
- Python -m coverage report
One of the common issues you may encounter with Python test automation is test breakage due to interdependencies. Basically, your test fails because your one test relies on the data or environment condition of other tests. So, if one test gets modified, other tests fail as a result of the chain reaction the change causes. To solve this, isolate all the tests so that each starts unconnected to the others. This way, you can debug without dealing with its ripple effect on other tests.
Here's how to ensure test isolation using setup and teardown logic on Pytest;
import pytest
@pytest.fixture
def clean_environment():
# Setup: Prepare a clean state
print("Setting up a clean environment.")
yield
# Teardown: Reset environment
print("Cleaning up after the test.")
def test_example(clean_environment):
assert 2 + 2 == 4
Note that the Pytest fixture ensures that the resources you use are cleaned up after each test. This allows you to restore the environment to its original state after the test has been executed.
I'll highlight a couple of best practices to become more efficient in Python test automation;
Automation testing can be challenging, especially when dealing with a series of seemingly unsolvable errors. I've discovered that test automation is less buggy when using the testing pyramid. Here, you focus on unit and integration tests before E2E tests. With the tips and practices I've highlighted, you can efficiently run continuous regression testing in a CI/CD environment and get immediate feedback before you deploy a code.