Fundamentals 11 min read

Understanding Python Iterables and Iterators and Their Use in Automated Testing

This article explains the difference between iterables and iterators in Python, demonstrates how to implement them with custom classes, shows basic usage with built‑in collections, and provides multiple automation‑testing examples using generators and iterator patterns to handle dynamic test data and pagination.

Test Development Learning Exchange
Test Development Learning Exchange
Test Development Learning Exchange
Understanding Python Iterables and Iterators and Their Use in Automated Testing

Introduction In Python, an iterable is an object that implements the __iter__() method, while an iterator implements the __next__() method. Understanding their distinction helps write clearer code.

Iterable An iterable returns an iterator via __iter__() . Example:

class MyIterable:
    def __init__(self, data):
        self.data = data
    def __iter__(self):
        return iter(self.data)

# Create an iterable instance
my_iterable = MyIterable([1, 2, 3])
for item in my_iterable:
    print(item)  # 输出: 1 2 3

Iterator An iterator provides the __next__() method to retrieve the next element and raises StopIteration when exhausted. Example:

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.position = 0
    def __next__(self):
        if self.position >= len(self.data):
            raise StopIteration
        item = self.data[self.position]
        self.position += 1
        return item

# Create an iterator instance
my_iterator = MyIterator([1, 2, 3])
print(next(my_iterator))  # 输出: 1
print(next(my_iterator))  # 输出: 2
print(next(my_iterator))  # 输出: 3
# print(next(my_iterator))  # 抛出 StopIteration

Relationship An iterable’s __iter__() returns an iterator, which implements the actual iteration logic via __next__() .

Basic Combined Example A custom iterable that returns a custom iterator:

class MyIterable:
    def __init__(self, data):
        self.data = data
    def __iter__(self):
        return MyIterator(self.data)

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.position = 0
    def __next__(self):
        if self.position >= len(self.data):
            raise StopIteration
        item = self.data[self.position]
        self.position += 1
        return item

my_iterable = MyIterable([1, 2, 3])
for item in my_iterable:
    print(item)  # 输出: 1 2 3

Built‑in Iterable Types Lists, tuples, sets, and dictionaries are all iterables. Examples:

for item in [1, 2, 3]:
    print(item)  # 输出: 1 2 3

d = {'a': 1, 'b': 2}
for key in d:
    print(key)  # 输出: a b

for value in d.values():
    print(value)  # 输出: 1 2

for item in d.items():
    print(item)  # 输出: ('a', 1) ('b', 2)

Automation‑Related Examples

Example 1: Generator for Dynamic Test Data

import requests
import yaml

def generate_test_cases(filename):
    with open(filename, 'r') as stream:
        test_cases = yaml.safe_load(stream)['tests']
        for test_case in test_cases:
            yield test_case

def test_api_with_generator():
    generator = generate_test_cases('data/api_tests.yaml')
    for test_case in generator:
        url = test_case['url']
        method = test_case['method']
        headers = test_case.get('headers', {})
        body = test_case.get('body', {})
        expected_status = test_case['expected_status']
        response = requests.request(method, url, headers=headers, json=body)
        assert response.status_code == expected_status, f"Expected status code {expected_status}, got {response.status_code}"

test_api_with_generator()

Example 2: Iterator for Paginated Data

import requests

class PageIterator:
    def __init__(self, base_url, params):
        self.base_url = base_url
        self.params = params
        self.page = 1
    def __iter__(self):
        return self
    def __next__(self):
        response = requests.get(self.base_url, params={**self.params, "page": self.page})
        if response.status_code != 200:
            raise RuntimeError(f"Request failed with status code {response.status_code}")
        data = response.json()
        if not data['results']:
            raise StopIteration
        self.page += 1
        return data['results']

def test_pagination():
    base_url = "https://api.example.com/data"
    params = {"limit": 10}
    page_iterator = PageIterator(base_url, params)
    for page_data in page_iterator:
        print(page_data)

test_pagination()

Example 3: Iterator for Multi‑Environment Testing

import requests
import yaml

class EnvironmentIterator:
    def __init__(self, filename):
        with open(filename, 'r') as stream:
            self.environments = yaml.safe_load(stream)['environments']
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.index < len(self.environments):
            env = self.environments[self.index]
            self.index += 1
            return env
        else:
            raise StopIteration

def test_environments():
    environment_iterator = EnvironmentIterator('data/environments.yaml')
    for env in environment_iterator:
        base_url = env['base_url']
        response = requests.get(f"{base_url}/health")
        assert response.status_code == 200, f"Health check failed for {base_url}"

test_environments()

Example 4: Generator Expression for Simplified Test Logic

import requests
import yaml

def test_api_with_generator_expression():
    test_cases = (
        {'url': "https://api.example.com/user/123", 'method': "GET", 'headers': {"Authorization": "Bearer token1"}, 'expected_status': 200},
        {'url': "https://api.example.com/user", 'method': "POST", 'headers': {"Authorization": "Bearer token2"}, 'body': {"name": "Jane Doe", "email": "[email protected]"}, 'expected_status': 201}
    )
    for test_case in test_cases:
        response = requests.request(test_case['method'], test_case['url'], headers=test_case.get('headers'), json=test_case.get('body'))
        assert response.status_code == test_case['expected_status'], f"Expected status code {test_case['expected_status']}, got {response.status_code}"

test_api_with_generator_expression()

Example 5: Iterator for Dynamic Parameter Combinations

import requests
import itertools

class ParameterCombinations:
    def __init__(self, urls, methods, headers, bodies, expected_statuses):
        self.urls = urls
        self.methods = methods
        self.headers = headers
        self.bodies = bodies
        self.expected_statuses = expected_statuses
        self.combinations = itertools.product(self.urls, self.methods, self.headers, self.bodies, self.expected_statuses)
        self.counter = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.counter < len(self.combinations):
            url, method, header, body, expected_status = self.combinations[self.counter]
            test_case = {
                'url': url,
                'method': method,
                'headers': header,
                'body': body,
                'expected_status': expected_status
            }
            self.counter += 1
            return test_case
        else:
            raise StopIteration

def test_parameter_combinations():
    urls = ["https://api.example.com/user/123", "https://api.example.com/user/456"]
    methods = ["GET", "POST"]
    headers = [{"Authorization": "Bearer token1"}, {"Authorization": "Bearer token2"}]
    bodies = [{}, {"name": "Jane Doe", "email": "[email protected]"}]
    expected_statuses = [200, 201]
    combinations_iterator = ParameterCombinations(urls, methods, headers, bodies, expected_statuses)
    for test_case in combinations_iterator:
        response = requests.request(test_case['method'], test_case['url'], headers=test_case['headers'], json=test_case.get('body'))
        assert response.status_code == test_case['expected_status'], f"Expected status code {test_case['expected_status']}, got {response.status_code}"

test_parameter_combinations()

Conclusion The examples demonstrate how Python’s iteration protocol—iterables, iterators, generators, and generator expressions—can be leveraged in API automation testing to manage complex data flows, reduce memory usage, and simplify test logic, ultimately improving code readability, maintainability, and flexibility.

PythontestingIteratorsAutomation TestingGeneratorsIterables
Test Development Learning Exchange
Written by

Test Development Learning Exchange

Test Development Learning Exchange

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.