This page shows the source code for Session-3-5-Test.py in browser-friendly HTML format. It was generated automatically from the original Python file.
"""
Test suite for Advanced Session 5
Demonstrates pytest features and best practices
Run with: pytest test_session5.py -v
Coverage: pytest test_session5.py --cov=advanced_session5_examples
"""
import pytest
from advanced_session5_examples import (
add, divide, is_prime, reverse_string, calculate_average,
BankAccount, DataProcessor, Config,
format_name, parse_value, validate_email
)
# ============================================
# PART 1: Basic Function Tests
# ============================================
class TestBasicFunctions:
"""Test basic mathematical and string functions."""
def test_add_positive_numbers(self):
"""Test adding two positive numbers."""
assert add(2, 3) == 5
assert add(10, 20) == 30
def test_add_negative_numbers(self):
"""Test adding negative numbers."""
assert add(-5, -3) == -8
assert add(-10, 5) == -5
def test_add_zero(self):
"""Test adding with zero."""
assert add(0, 0) == 0
assert add(5, 0) == 5
def test_divide_normal(self):
"""Test normal division."""
assert divide(10, 2) == 5.0
assert divide(15, 3) == 5.0
def test_divide_by_zero(self):
"""Test division by zero raises error."""
with pytest.raises(ValueError, match="Cannot divide by zero"):
divide(10, 0)
def test_divide_negative(self):
"""Test division with negative numbers."""
assert divide(-10, 2) == -5.0
assert divide(10, -2) == -5.0
# ============================================
# PART 2: Parametrized Tests
# ============================================
class TestParametrized:
"""Demonstrate parametrized testing."""
@pytest.mark.parametrize("a, b, expected", [
(2, 3, 5),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
(-5, -3, -8),
])
def test_add_multiple_cases(self, a, b, expected):
"""Test add function with multiple parameter sets."""
assert add(a, b) == expected
@pytest.mark.parametrize("number, expected", [
(2, True),
(3, True),
(5, True),
(7, True),
(11, True),
(4, False),
(6, False),
(9, False),
(15, False),
])
def test_is_prime_multiple_cases(self, number, expected):
"""Test prime checking with multiple numbers."""
assert is_prime(number) == expected
@pytest.mark.parametrize("string, expected", [
("hello", "olleh"),
("", ""),
("a", "a"),
("Python", "nohtyP"),
("12345", "54321"),
])
def test_reverse_string_cases(self, string, expected):
"""Test string reversal with various inputs."""
assert reverse_string(string) == expected
# ============================================
# PART 3: Fixtures
# ============================================
@pytest.fixture
def sample_numbers():
"""Provide sample number list for testing."""
return [10, 20, 30, 40, 50]
@pytest.fixture
def bank_account():
"""Provide a bank account instance."""
return BankAccount("Test User", 1000)
@pytest.fixture
def data_processor():
"""Provide a data processor instance."""
processor = DataProcessor()
processor.add_data(10)
processor.add_data(20)
processor.add_data(30)
return processor
class TestWithFixtures:
"""Tests using fixtures."""
def test_calculate_average(self, sample_numbers):
"""Test average calculation with fixture."""
result = calculate_average(sample_numbers)
assert result == 30.0
def test_bank_account_deposit(self, bank_account):
"""Test deposit with fixture."""
initial = bank_account.get_balance()
bank_account.deposit(500)
assert bank_account.get_balance() == initial + 500
def test_bank_account_withdraw(self, bank_account):
"""Test withdrawal with fixture."""
initial = bank_account.get_balance()
bank_account.withdraw(300)
assert bank_account.get_balance() == initial - 300
def test_data_processor_statistics(self, data_processor):
"""Test statistics calculation with fixture."""
stats = data_processor.get_statistics()
assert stats['mean'] == 20.0
assert stats['min'] == 10
assert stats['max'] == 30
assert stats['count'] == 3
# ============================================
# PART 4: Testing Exceptions
# ============================================
class TestExceptions:
"""Test that functions raise appropriate exceptions."""
def test_is_prime_invalid_input(self):
"""Test is_prime raises error for invalid input."""
with pytest.raises(ValueError):
is_prime(1)
with pytest.raises(ValueError):
is_prime(0)
with pytest.raises(ValueError):
is_prime(-5)
def test_reverse_string_type_error(self):
"""Test reverse_string raises TypeError for non-string."""
with pytest.raises(TypeError):
reverse_string(123)
with pytest.raises(TypeError):
reverse_string([1, 2, 3])
def test_calculate_average_empty_list(self):
"""Test average raises error for empty list."""
with pytest.raises(ValueError, match="empty list"):
calculate_average([])
def test_bank_account_negative_initial(self):
"""Test bank account rejects negative initial balance."""
with pytest.raises(ValueError):
BankAccount("Test", -100)
def test_bank_account_invalid_deposit(self):
"""Test deposit rejects invalid amounts."""
account = BankAccount("Test", 1000)
with pytest.raises(ValueError):
account.deposit(0)
with pytest.raises(ValueError):
account.deposit(-50)
def test_bank_account_insufficient_funds(self):
"""Test withdraw raises error for insufficient funds."""
account = BankAccount("Test", 100)
with pytest.raises(ValueError, match="Insufficient funds"):
account.withdraw(200)
# ============================================
# PART 5: Testing Class Behavior
# ============================================
class TestBankAccount:
"""Comprehensive tests for BankAccount class."""
def test_initialization(self):
"""Test account initialization."""
account = BankAccount("Alice", 500)
assert account.owner == "Alice"
assert account.balance == 500
def test_deposit_increases_balance(self):
"""Test deposit increases balance correctly."""
account = BankAccount("Bob", 1000)
new_balance = account.deposit(500)
assert new_balance == 1500
assert account.get_balance() == 1500
def test_withdraw_decreases_balance(self):
"""Test withdraw decreases balance correctly."""
account = BankAccount("Charlie", 1000)
new_balance = account.withdraw(300)
assert new_balance == 700
assert account.get_balance() == 700
def test_multiple_transactions(self):
"""Test multiple deposits and withdrawals."""
account = BankAccount("Diana", 1000)
account.deposit(500)
account.withdraw(200)
account.deposit(300)
assert account.get_balance() == 1600
def test_str_representation(self):
"""Test string representation."""
account = BankAccount("Eve", 1000)
assert "Eve" in str(account)
assert "1000" in str(account)
class TestDataProcessor:
"""Comprehensive tests for DataProcessor class."""
def test_add_data(self):
"""Test adding data points."""
processor = DataProcessor()
processor.add_data(10)
processor.add_data(20)
assert len(processor.data) == 2
def test_clear_data(self):
"""Test clearing data."""
processor = DataProcessor()
processor.add_data(10)
processor.add_data(20)
processor.clear()
assert len(processor.data) == 0
def test_statistics_empty_raises_error(self):
"""Test statistics on empty data raises error."""
processor = DataProcessor()
with pytest.raises(ValueError):
processor.get_statistics()
def test_statistics_calculation(self):
"""Test statistics are calculated correctly."""
processor = DataProcessor()
for value in [10, 20, 30, 40, 50]:
processor.add_data(value)
stats = processor.get_statistics()
assert stats['mean'] == 30.0
assert stats['min'] == 10
assert stats['max'] == 50
assert stats['count'] == 5
def test_filter_above_threshold(self):
"""Test filtering data."""
processor = DataProcessor()
for value in [10, 20, 30, 40, 50]:
processor.add_data(value)
filtered = processor.filter_above_threshold(25)
assert filtered == [30, 40, 50]
# ============================================
# PART 6: Testing Utility Functions
# ============================================
class TestUtilityFunctions:
"""Test utility functions with type hints."""
def test_format_name_without_middle(self):
"""Test name formatting without middle name."""
result = format_name("John", "Doe")
assert result == "John Doe"
def test_format_name_with_middle(self):
"""Test name formatting with middle name."""
result = format_name("John", "Doe", "Q")
assert result == "John Q Doe"
@pytest.mark.parametrize("value, expected", [
(42, 42.0),
("3.14", 3.14),
(2.5, 2.5),
("100", 100.0),
])
def test_parse_value_valid(self, value, expected):
"""Test parsing valid values."""
assert parse_value(value) == expected
def test_parse_value_invalid(self):
"""Test parsing invalid values raises error."""
with pytest.raises(ValueError):
parse_value("not a number")
@pytest.mark.parametrize("email, expected", [
("test@example.com", True),
("user@domain.co.uk", True),
("invalid", False),
("@example.com", False),
("test@", False),
])
def test_validate_email(self, email, expected):
"""Test email validation."""
assert validate_email(email) == expected
# ============================================
# PART 7: Testing Configuration
# ============================================
class TestConfig:
"""Test configuration class."""
def test_default_values(self):
"""Test default configuration values."""
config = Config()
assert config.debug is False
assert config.log_level == "INFO"
assert config.max_retries == 3
assert config.timeout == 30.0
def test_load_from_dict(self):
"""Test loading configuration from dictionary."""
config = Config()
config.load_from_dict({
'debug': True,
'max_retries': 5
})
assert config.debug is True
assert config.max_retries == 5
def test_validate_valid_config(self):
"""Test validation accepts valid configuration."""
config = Config()
assert config.validate() is True
def test_validate_invalid_max_retries(self):
"""Test validation rejects negative max_retries."""
config = Config()
config.max_retries = -1
assert config.validate() is False
def test_validate_invalid_timeout(self):
"""Test validation rejects non-positive timeout."""
config = Config()
config.timeout = 0
assert config.validate() is False
def test_validate_invalid_log_level(self):
"""Test validation rejects invalid log level."""
config = Config()
config.log_level = "INVALID"
assert config.validate() is False
def test_to_dict(self):
"""Test converting config to dictionary."""
config = Config()
config_dict = config.to_dict()
assert isinstance(config_dict, dict)
assert 'debug' in config_dict
assert 'log_level' in config_dict
# ============================================
# PART 8: Marks and Skipping
# ============================================
class TestMarks:
"""Demonstrate pytest marks."""
@pytest.mark.slow
def test_slow_operation(self):
"""Mark a slow test."""
# This would be a slow operation
import time
# time.sleep(2) # Uncomment to make it actually slow
assert True
@pytest.mark.skip(reason="Not implemented yet")
def test_future_feature(self):
"""Skip a test for future feature."""
assert False # This won't run
@pytest.mark.skipif(True, reason="Conditional skip")
def test_conditional(self):
"""Conditionally skip a test."""
assert False # This won't run
@pytest.mark.xfail(reason="Known bug")
def test_known_bug(self):
"""Mark a test as expected to fail."""
assert 1 == 2 # Expected to fail
# ============================================
# Summary
# ============================================
"""
Test Suite Summary:
This test suite demonstrates:
1. Basic function testing
2. Parametrized tests
3. Fixtures for setup/teardown
4. Exception testing
5. Class behavior testing
6. Utility function testing
7. Configuration testing
8. Test marks and skipping
Run commands:
- pytest test_session5.py # Run all tests
- pytest test_session5.py -v # Verbose output
- pytest test_session5.py::TestBasicFunctions # Run specific class
- pytest test_session5.py -k "add" # Run tests matching 'add'
- pytest test_session5.py -m slow # Run tests marked as slow
- pytest test_session5.py --cov # With coverage report
- pytest test_session5.py -x # Stop on first failure
- pytest test_session5.py --tb=short # Short traceback format
Coverage:
- pytest --cov=advanced_session5_examples --cov-report=html
This will generate htmlcov/index.html with detailed coverage report.
"""