Загрузка данных
Вот полный набор тестов для `RockPaperScissors.play`. Проблема в том, что компьютер выбирает случайно, поэтому в тестах его выбор мокается через `random.choice` — иначе нельзя гарантировать все комбинации.
**Файл `rock_paper_scissors.py` (тестируемый класс):**
```python
import random
class RockPaperScissors:
CHOICES = ('rock', 'paper', 'scissors')
BEATS = {'rock': 'scissors', 'paper': 'rock', 'scissors': 'paper'}
def play(self, player_choice):
if not isinstance(player_choice, str):
raise ValueError("Выбор должен быть строкой")
player_choice = player_choice.lower()
if player_choice not in self.CHOICES:
raise ValueError("Допустимые значения: 'rock', 'paper', 'scissors'")
computer_choice = random.choice(self.CHOICES)
if player_choice == computer_choice:
return 'tie'
if self.BEATS[player_choice] == computer_choice:
return 'win'
return 'lose'
```
**Файл `test_rock_paper_scissors.py`:**
```python
import pytest
from unittest.mock import patch
from rock_paper_scissors import RockPaperScissors
@pytest.fixture
def game():
return RockPaperScissors()
class TestPlayValidInput:
"""Проверка всех комбинаций результатов."""
# Параметризация: (выбор игрока, выбор компьютера, ожидаемый результат)
@pytest.mark.parametrize("player, computer, expected", [
# Победы игрока (комбинации: камень > ножницы, бумага > камень, ножницы > бумага)
('rock', 'scissors', 'win'),
('paper', 'rock', 'win'),
('scissors', 'paper', 'win'),
# Поражения игрока
('rock', 'paper', 'lose'),
('paper', 'scissors', 'lose'),
('scissors', 'rock', 'lose'),
# Ничьи (3 комбинации)
('rock', 'rock', 'tie'),
('paper', 'paper', 'tie'),
('scissors', 'scissors', 'tie'),
])
def test_all_combinations(self, game, player, computer, expected):
"""Каждая пара ходов даёт корректный результат: win/lose/tie."""
with patch('random.choice', return_value=computer):
assert game.play(player) == expected
def test_all_combinations_covered(self, game):
"""Полный перебор 3×3 комбинаций через random.seed."""
results = set()
for player in game.CHOICES:
for computer in game.CHOICES:
with patch('random.choice', return_value=computer):
results.add(game.play(player))
assert results == {'win', 'lose', 'tie'}
class TestPlayInvalidInput:
"""Ошибки при неправильном выборе — ValueError."""
@pytest.mark.parametrize("bad_input", [
'Rocky', # опечатка / несуществующий выбор
'камень', # нелатиница
'rock paper', # несколько слов
'', # пустая строка
' rock', # пробелы — если класс их не тримит
'rock ', # замыкающий пробел
123, # не строка
None, # None
True, # bool (подкласс int!)
[], # список
{}, # словарь
])
def test_invalid_choice_raises_value_error(self, game, bad_input):
with pytest.raises(ValueError):
game.play(bad_input)
def test_valid_values_do_not_raise(self, game):
"""Все три допустимых значения не вызывают исключений."""
for choice in game.CHOICES:
with patch('random.choice', return_value=choice):
game.play(choice) # не должно быть ValueError
class TestRandomness:
"""Проверка, что компьютер действительно выбирает случайно."""
def test_random_choice_is_used(self, game):
with patch('random.choice') as mock_choice:
mock_choice.return_value = 'rock'
game.play('rock')
mock_choice.assert_called_once_with(game.CHOICES)
```
**Что здесь проверяется:**
- **Все 9 комбинаций** (3×3) через `@pytest.mark.parametrize` — победы, поражения и ничьи покрыты явно.
- **Некорректный ввод** — нестроковые типы (`123`, `None`, список), опечатки, кириллица, пустая строка. Обрати внимание на `True` — это подкласс `int`, и `isinstance(True, str)` корректно отсекает его.
- **Мокирование** через `patch('random.choice', ...)` — без этого результат непредсказуем.
- Трим пробелов зависит от реализации: если твой класс делает `.strip()`, убери `' rock'` и `'rock '` из списка невалидных значений и перенеси в валидные.
Если у тебя метод генерации случайного выбора называется иначе (например, внутренний метод класса), поправь путь в `patch` — скажи код реализации, адаптирую тесты.