diff --git a/README.md b/README.md index 51e626b..0ac26f3 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 文件结构 -程序主要分为两部分,游戏主体和分析工具 +程序主要分为三部分,游戏主体、分析工具和测试。 ### 游戏主体 @@ -26,6 +26,30 @@ `json_convert.py` 用于将json游戏记录转为可读文本 +### 测试 + +所有的测试位于`test`文件夹中 + +运行前先安装 + +```bash +pip install -r ./test/requirements.txt +``` + +运行全部测试: + +```bash + +pytest tests/ -v --cov=. + +``` + +生成HTML报告: + +```bash +pytest --cov=. --cov-report=html +``` + ## 配置 使用conda环境配置相应依赖包: diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..bf0fc52 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,9 @@ +import pytest + +@pytest.fixture(autouse=True) +def mock_llm_calls(monkeypatch): + """Mock all LLM API calls""" + def mock_chat(*args, **kwargs): + return ("mocked response", "") + + monkeypatch.setattr("llm_client.LLMClient.chat", mock_chat) \ No newline at end of file diff --git a/test/requirement.txt b/test/requirement.txt new file mode 100644 index 0000000..d7cca1e --- /dev/null +++ b/test/requirement.txt @@ -0,0 +1,2 @@ +pytest>=7.0 +pytest-cov>=4.0 \ No newline at end of file diff --git a/test/test_game.py b/test/test_game.py new file mode 100644 index 0000000..9197937 --- /dev/null +++ b/test/test_game.py @@ -0,0 +1,23 @@ +import pytest +from game import Game +from player import Player + +@pytest.fixture +def sample_game(): + return Game([ + {"name": "P1", "model": "m1"}, + {"name": "P2", "model": "m2"} + ]) + +def test_game_initialization(sample_game): + assert len(sample_game.players) == 2 + assert sample_game.game_record is not None + assert sample_game.deck == [] + +def test_handle_system_challenge(sample_game, mocker): + mocker.patch.object(sample_game, 'check_other_players_no_cards', return_value=True) + current_player = sample_game.players[0] + current_player.hand = ["A", "B"] + + sample_game.handle_system_challenge(current_player) + assert len(current_player.hand) == 0 \ No newline at end of file diff --git a/test/test_game_analyze.py b/test/test_game_analyze.py new file mode 100644 index 0000000..709509f --- /dev/null +++ b/test/test_game_analyze.py @@ -0,0 +1,32 @@ +import pytest +import tempfile +import json +import os +from game_analyze import analyze_game_records + +@pytest.fixture +def sample_game_data(): + return { + "winner": "PlayerA", + "rounds": [{ + "play_history": [{ + "player_name": "PlayerA", + "next_player": "PlayerB", + "was_challenged": True, + "challenge_result": True + }], + "round_result": { + "shooter_name": "PlayerA", + "bullet_hit": True + } + }] + } + +def test_analysis_calculation(sample_game_data): + with tempfile.TemporaryDirectory() as tmpdir: + with open(os.path.join(tmpdir, "test.json"), 'w') as f: + json.dump(sample_game_data, f) + + stats = analyze_game_records(tmpdir) + assert stats['wins']['PlayerA'] == 1 + assert stats['shots_fired']['PlayerA'] == 1 \ No newline at end of file diff --git a/test/test_json_convert.py b/test/test_json_convert.py new file mode 100644 index 0000000..423d9e2 --- /dev/null +++ b/test/test_json_convert.py @@ -0,0 +1,37 @@ +import pytest +import tempfile +import json +import os +from json_convert import process_game_records + +@pytest.fixture +def sample_json_game(): + return { + "game_id": "TEST123", + "rounds": [{ + "target_card": "A", + "play_history": [{ + "player_name": "P1", + "played_cards": ["A"], + "remaining_cards": [] + }] + }] + } + +def test_json_conversion(sample_json_game): + with tempfile.TemporaryDirectory() as input_dir, \ + tempfile.TemporaryDirectory() as output_dir: + + # Create test input + input_path = os.path.join(input_dir, "test.json") + with open(input_path, 'w') as f: + json.dump(sample_json_game, f) + + process_game_records(input_dir, output_dir) + + # Verify output + output_path = os.path.join(output_dir, "test.txt") + assert os.path.exists(output_path) + with open(output_path, 'r') as f: + content = f.read() + assert "游戏ID: TEST123" in content \ No newline at end of file diff --git a/test/test_player.py b/test/test_player.py new file mode 100644 index 0000000..2c5f8ed --- /dev/null +++ b/test/test_player.py @@ -0,0 +1,45 @@ +import pytest +import json +from unittest.mock import Mock, patch +from player import Player + +@pytest.fixture +def mock_player(): + player = Player("TestPlayer", "test-model") + player.hand = ["A", "B", "C"] + player.llm_client = Mock() + return player + +def test_choose_cards_to_play_valid_response(mock_player): + mock_response = json.dumps({ + "played_cards": ["A"], + "behavior": "保守出牌", + "play_reason": "测试原因" + }) + mock_player.llm_client.chat.return_value = (mock_response, "") + + result = mock_player.choose_cards_to_play("", "", "") + assert "played_cards" in result + assert set(result["played_cards"]).issubset(mock_player.hand) + +def test_decide_challenge_retry_logic(mock_player): + mock_player.llm_client.chat.side_effect = [ + ("invalid_response", ""), + ('{"was_challenged": true, "challenge_reason": ""}', "") + ] + + result, _ = mock_player.decide_challenge("", "", "", "", "") + assert mock_player.llm_client.chat.call_count == 2 + assert result["was_challenged"] is True + +def test_reflect_updates_opinions(mock_player): + mock_player.llm_client.chat.return_value = ("新的印象分析", "") + + mock_player.reflect( + ["OtherPlayer"], + "Round Info", + "Action Info", + "Result Info" + ) + + assert "OtherPlayer" in mock_player.opinions \ No newline at end of file