Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Полиморфизм
Полиморфизм (от греч. «множество форм») — это ключевой принцип объектно-ориентированного программирования (ООП), который позволяет объектам разных типов использоваться через один интерфейс. Это критично для написания гибких, масштабируемых и поддерживаемых тестов.
Что такое полиморфизм
Полиморфизм — это возможность объекта принимать множество форм или способность функции работать с объектами разных типов. Он позволяет писать код, который работает с абстракциями, а не с конкретными реализациями.
Виды полиморфизма
1. Параметрический полиморфизм (Generic)
Позволяет функциям и классам работать с данными разных типов без потери типизации.
from typing import TypeVar, List
T = TypeVar('T')
def get_first(items: List[T]) -> T:
return items[0]
# Работает с разными типами
first_int = get_first([1, 2, 3]) # int
first_str = get_first(['a', 'b', 'c']) # str
2. Полиморфизм подтипов (Subtyping)
Позволяет подклассам переопределять методы родительского класса.
class APIClient:
def send_request(self, method: str, url: str) -> dict:
raise NotImplementedError
class RESTClient(APIClient):
def send_request(self, method: str, url: str) -> dict:
# Реализация для REST
return requests.request(method, url).json()
class SOAPClient(APIClient):
def send_request(self, method: str, url: str) -> dict:
# Реализация для SOAP
return parse_soap_response()
# Использование полиморфизма
def test_api(client: APIClient):
response = client.send_request('GET', '/users')
assert response is not None
# Одна функция работает с разными клиентами
test_api(RESTClient()) # Работает
test_api(SOAPClient()) # Работает
3. Перегрузка методов (Method Overloading)
Возможность определить несколько методов с одним именем, но разными параметрами. В Python это реализуется через значения по умолчанию или *args, **kwargs.
class TestData:
def create_user(self, name: str, email: str = None, age: int = None) -> dict:
user = {'name': name}
if email:
user['email'] = email
if age:
user['age'] = age
return user
data = TestData()
# Разные варианты вызова
user1 = data.create_user('John')
user2 = data.create_user('Jane', 'jane@example.com')
user3 = data.create_user('Bob', 'bob@example.com', 30)
4. Динамический полиморфизм (Duck Typing)
«Если ходит как утка и крякает как утка — это утка». Объекты разных типов, реализующие одинаковый интерфейс, могут использоваться одинаково.
class Logger:
def log(self, message: str):
raise NotImplementedError
class ConsoleLogger(Logger):
def log(self, message: str):
print(message)
class FileLogger(Logger):
def log(self, message: str):
with open('log.txt', 'a') as f:
f.write(message + '\\n')
def run_test(logger: Logger):
logger.log('Test started')
# Выполнение теста
logger.log('Test passed')
# Работает с любым логгером
run_test(ConsoleLogger())
run_test(FileLogger())
Полиморфизм в тестировании
Мокирование зависимостей через полиморфизм
class UserService:
def __init__(self, repository):
self.repository = repository
def get_user(self, user_id: int):
return self.repository.find_by_id(user_id)
class UserRepository:
def find_by_id(self, user_id: int):
# Реальное обращение к БД
pass
class MockUserRepository:
def find_by_id(self, user_id: int):
# Фиксированные данные для тестирования
return {'id': user_id, 'name': 'Test User'}
def test_get_user():
# Используем мок вместо реального репозитория
mock_repo = MockUserRepository()
service = UserService(mock_repo)
user = service.get_user(1)
assert user['name'] == 'Test User'
Использование ABC (Abstract Base Classes)
from abc import ABC, abstractmethod
class HTTPClient(ABC):
@abstractmethod
def get(self, url: str) -> dict:
pass
@abstractmethod
def post(self, url: str, data: dict) -> dict:
pass
class RealHTTPClient(HTTPClient):
def get(self, url: str) -> dict:
return requests.get(url).json()
def post(self, url: str, data: dict) -> dict:
return requests.post(url, json=data).json()
class MockHTTPClient(HTTPClient):
def __init__(self):
self.requests = []
def get(self, url: str) -> dict:
self.requests.append(('GET', url))
return {'status': 200}
def post(self, url: str, data: dict) -> dict:
self.requests.append(('POST', url, data))
return {'status': 201}
def make_api_call(client: HTTPClient):
return client.get('/api/users')
# Работает с обоими
result1 = make_api_call(RealHTTPClient())
result2 = make_api_call(MockHTTPClient())
Преимущества полиморфизма
- Гибкость — код работает с интерфейсами, а не с конкретными реализациями
- Переиспользуемость — один код работает с разными типами
- Тестируемость — легко подменять реальные объекты на моки
- Расширяемость — добавлять новые типы без изменения существующего кода
- Maintainability — проще поддерживать и обновлять код
Практический пример для QA
class APITester:
def __init__(self, client):
self.client = client
def test_users_endpoint(self):
response = self.client.get('/api/users')
assert response['status'] == 200
assert isinstance(response['data'], list)
# Один тест работает с разными клиентами
tester = APITester(RealHTTPClient()) # Реальное тестирование
tester.test_users_endpoint()
tester = APITester(MockHTTPClient()) # Юнит тест
tester.test_users_endpoint()
Полиморфизм — это основа SOLID принципов и позволяет писать код, который остаётся гибким и поддерживаемым при росте сложности проекта.