0
0
Selenium Pythontesting~15 mins

Test functions and classes in Selenium Python - Deep Dive

Choose your learning style9 modes available
Overview - Test functions and classes
What is it?
Test functions and classes are ways to organize and run automated tests in software. A test function is a small piece of code that checks if a part of the software works as expected. A test class groups related test functions together, making tests easier to manage and reuse. These help find problems early by running checks automatically.
Why it matters
Without test functions and classes, testing would be slow, manual, and error-prone. Developers might miss bugs that cause software to break later, frustrating users and costing time and money. Automated tests organized in functions and classes make testing fast, repeatable, and reliable, helping teams deliver better software with confidence.
Where it fits
Before learning test functions and classes, you should know basic Python programming and how Selenium controls browsers. After this, you can learn about test frameworks like pytest or unittest, which use these functions and classes to run tests automatically and report results.
Mental Model
Core Idea
Test functions and classes are like checklists and folders that organize and run software checks automatically and clearly.
Think of it like...
Imagine you are a chef testing recipes. Each test function is like a single recipe you try to make sure it tastes right. A test class is like a cookbook section grouping similar recipes, like all desserts together. This way, you can test each recipe and find problems quickly, and organize them so you don’t lose track.
┌───────────────┐
│ Test Class   │
│ ┌───────────┐ │
│ │ Test Func │ │
│ ├───────────┤ │
│ │ Test Func │ │
│ └───────────┘ │
└───────────────┘

Test Class groups multiple Test Functions
Build-Up - 7 Steps
1
FoundationWriting a simple test function
🤔
Concept: Learn how to write a basic test function that checks one thing.
A test function is a Python function whose name starts with 'test_'. Inside, you use Selenium commands to open a webpage and check something, like the page title. Use assert statements to verify expected results. Example: def test_google_title(driver): driver.get('https://www.google.com') assert 'Google' in driver.title Here, driver is a Selenium WebDriver instance controlling the browser.
Result
When run, this test opens Google and passes if the title contains 'Google'. If not, it fails and shows an error.
Understanding that a test function is just a Python function with checks helps you write many small, focused tests that are easy to read and maintain.
2
FoundationUsing assertions to check results
🤔
Concept: Assertions are how tests confirm software behaves correctly.
Inside a test function, use assert statements to compare actual results with expected ones. If the assert fails, the test fails. Example: assert element.is_displayed() # checks if element is visible assert page_title == 'Home' # exact match check Assertions stop the test immediately if the condition is false.
Result
Tests report pass if all assertions hold true, or fail with details if any assertion fails.
Knowing assertions are the test's decision points helps you focus tests on meaningful checks that catch bugs.
3
IntermediateGrouping tests with classes
🤔Before reading on: do you think test classes run tests automatically or need manual calls? Commit to your answer.
Concept: Test classes group related test functions and can share setup code.
A test class is a Python class whose name starts with 'Test'. Inside, you define multiple test functions. You can add setup methods to prepare the browser or data once for all tests. Example: class TestLogin: def setup_method(self): self.driver = webdriver.Chrome() def teardown_method(self): self.driver.quit() def test_valid_login(self): self.driver.get('https://example.com/login') # perform login steps assert 'Dashboard' in self.driver.title Test runners detect and run all test functions inside classes automatically.
Result
Tests inside the class run one by one, with setup and teardown running before and after each test.
Understanding classes let you organize tests logically and reuse setup code, making tests cleaner and faster to write.
4
IntermediateSharing setup with fixtures
🤔Before reading on: do you think setup code runs once per class or once per test function? Commit to your answer.
Concept: Fixtures provide reusable setup and cleanup for tests, improving efficiency and clarity.
In pytest, fixtures are functions that prepare test environments and can be shared across tests. Example: import pytest @pytest.fixture def driver(): driver = webdriver.Chrome() yield driver driver.quit() def test_google_title(driver): driver.get('https://www.google.com') assert 'Google' in driver.title Fixtures run setup before the test and cleanup after automatically.
Result
Tests using fixtures run with a fresh browser each time, ensuring isolation and no leftover state.
Knowing fixtures separate setup from test logic helps keep tests simple and reliable.
5
AdvancedParameterizing test functions
🤔Before reading on: do you think parameterized tests run once or multiple times with different data? Commit to your answer.
Concept: Parameterization runs the same test function multiple times with different inputs.
Using pytest, you can add @pytest.mark.parametrize to run a test with many data sets. Example: @pytest.mark.parametrize('search_term', ['apple', 'banana', 'cherry']) def test_search(driver, search_term): driver.get('https://example.com') search_box = driver.find_element(By.NAME, 'q') search_box.send_keys(search_term) search_box.submit() assert search_term in driver.title This runs test_search three times with different search terms.
Result
Test reports show each input as a separate test, helping find which data causes failure.
Understanding parameterization lets you test many cases efficiently without repeating code.
6
AdvancedUsing test classes for stateful tests
🤔
Concept: Test classes can hold state between methods to test sequences or workflows.
Sometimes tests need to share data or steps. You can store data in self variables inside a test class. Example: class TestWorkflow: def test_step1(self, driver): driver.get('https://example.com') self.token = driver.find_element(By.ID, 'token').text def test_step2(self, driver): driver.get(f'https://example.com/use?token={self.token}') assert 'Success' in driver.page_source Note: This requires careful test ordering or using setup to share state.
Result
Tests can simulate multi-step processes but risk dependency issues if order changes.
Knowing when and how to share state in classes helps test complex flows but requires discipline to avoid flaky tests.
7
ExpertCustomizing test discovery and execution
🤔Before reading on: do you think test runners find all tests by default or need configuration? Commit to your answer.
Concept: Test runners like pytest discover test functions and classes by naming conventions but can be customized for special cases.
By default, pytest finds functions named test_* and classes named Test*. You can change this with command-line options or plugins. Example: pytest --pyargs mypackage.tests You can also mark tests to skip or run conditionally: import pytest @pytest.mark.skip(reason='Not ready') def test_future_feature(): pass This controls which tests run and helps manage large suites.
Result
Test runs become flexible, allowing selective testing and integration with CI pipelines.
Understanding test discovery and control mechanisms empowers you to manage large test suites efficiently and avoid wasted time.
Under the Hood
Test runners scan your code files looking for functions and classes that match naming patterns like test_*. They load these into memory and run each test function in isolation, capturing assertion results. Setup and teardown methods or fixtures run before and after tests to prepare and clean up the environment. Failures raise exceptions that the runner catches to report test failures.
Why designed this way?
This design makes tests easy to write and discover without extra configuration. Naming conventions reduce setup overhead. Isolating tests prevents side effects between tests, improving reliability. Fixtures and setup methods provide flexible ways to share common code without repeating it.
┌───────────────┐
│ Test Runner   │
├───────────────┤
│ Scans files   │
│ Finds tests   │
│ Runs setup   │
│ Runs test fn │
│ Runs teardown│
│ Collects results│
└──────┬────────┘
       │
┌──────▼────────┐
│ Test Functions│
│ & Classes    │
└──────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do test classes run tests only if you call them manually? Commit yes or no.
Common Belief:Test classes are just containers and don’t run tests automatically.
Tap to reveal reality
Reality:Test runners automatically find and run test functions inside classes named with Test prefix without manual calls.
Why it matters:Believing this leads to writing tests that never run, giving false confidence that code is tested.
Quick: Do you think setup_method runs once per class or once per test? Commit your answer.
Common Belief:Setup methods run once per test class before all tests.
Tap to reveal reality
Reality:Setup methods like setup_method run before each test function, not once per class.
Why it matters:Misunderstanding this causes inefficient tests or leftover state between tests, leading to flaky results.
Quick: Do you think assertions stop the whole test suite or just the current test? Commit your answer.
Common Belief:An assertion failure stops all tests immediately.
Tap to reveal reality
Reality:An assertion failure stops only the current test function; other tests continue running.
Why it matters:Thinking otherwise may cause developers to run tests one by one, wasting time and missing other failures.
Quick: Do you think test functions can share state easily without classes? Commit yes or no.
Common Belief:Test functions can share data easily by using global variables.
Tap to reveal reality
Reality:Sharing state via globals is discouraged because it causes tests to depend on each other and become unreliable.
Why it matters:Ignoring this leads to flaky tests that pass or fail unpredictably, making debugging hard.
Expert Zone
1
Test classes can be combined with fixtures to create layered setups, allowing complex environments to be built incrementally.
2
Parameterizing tests with multiple arguments can create combinatorial explosion; managing this requires careful test design to avoid long test times.
3
Test discovery can be customized with plugins or hooks to support legacy codebases or non-standard naming, but this adds complexity.
When NOT to use
Avoid using test classes when tests are simple and independent; prefer plain test functions for clarity. For very large suites, consider splitting tests into modules or using test suites to manage execution order. Use fixtures instead of setup methods when possible for better reusability.
Production Patterns
In real projects, tests are organized by feature or page in classes, with fixtures providing browser setup. Parameterized tests cover multiple data inputs like form fields. Tests are run in CI pipelines with selective markers to run fast smoke tests or full regression suites.
Connections
Unit Testing
Test functions and classes are the building blocks of unit testing frameworks.
Understanding how to write and organize test functions and classes is essential to mastering unit testing, which focuses on small pieces of code.
Continuous Integration (CI)
Automated tests organized in functions and classes run in CI pipelines to catch bugs early.
Knowing test structure helps you write tests that integrate smoothly with CI tools, enabling fast feedback on code changes.
Scientific Experiment Design
Test functions resemble controlled experiments testing one variable at a time.
Seeing tests as experiments helps appreciate the importance of isolation, repeatability, and clear expected outcomes in software testing.
Common Pitfalls
#1Writing test functions that depend on each other’s results.
Wrong approach:def test_step1(): global token token = 'abc' def test_step2(): assert token == 'abc' # This fails if test_step1 does not run first.
Correct approach:Use fixtures or setup methods to share state explicitly: @pytest.fixture def token(): return 'abc' def test_step2(token): assert token == 'abc'
Root cause:Misunderstanding test isolation and relying on test execution order causes fragile tests.
#2Not quitting the browser after tests, causing resource leaks.
Wrong approach:def test_google(): driver = webdriver.Chrome() driver.get('https://google.com') assert 'Google' in driver.title # missing driver.quit()
Correct approach:def test_google(): driver = webdriver.Chrome() try: driver.get('https://google.com') assert 'Google' in driver.title finally: driver.quit()
Root cause:Forgetting cleanup steps leads to many browser instances running, slowing tests and machines.
#3Using broad exception catching that hides test failures.
Wrong approach:def test_element(): try: element = driver.find_element(By.ID, 'missing') except Exception: pass # hides failure
Correct approach:def test_element(): element = driver.find_element(By.ID, 'missing') # lets test fail if not found
Root cause:Trying to handle errors inside tests improperly masks real problems.
Key Takeaways
Test functions are small, focused checks that verify one behavior at a time using assertions.
Test classes group related tests and share setup code, improving organization and reducing repetition.
Fixtures and setup methods prepare the test environment cleanly and reliably before each test runs.
Parameterizing tests lets you run the same test logic with different data inputs efficiently.
Understanding test discovery and execution helps you write tests that run automatically and integrate with tools.