349 lines
8.7 KiB
Markdown
349 lines
8.7 KiB
Markdown
|
|
# Testing Guide - City Builder Game
|
||
|
|
|
||
|
|
This document provides comprehensive information about the testing framework for the City Builder multiplayer game.
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
### Prerequisites
|
||
|
|
1. **Start the game server** (required for tests):
|
||
|
|
```bash
|
||
|
|
python run.py
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Run tests** (in another terminal):
|
||
|
|
```bash
|
||
|
|
# Install test dependencies
|
||
|
|
pip install -r requirements.txt
|
||
|
|
|
||
|
|
# Run all tests
|
||
|
|
pytest
|
||
|
|
|
||
|
|
# Or use the test runner script
|
||
|
|
python run_tests.py
|
||
|
|
```
|
||
|
|
|
||
|
|
## Test Structure
|
||
|
|
|
||
|
|
### Test Files
|
||
|
|
- `tests/test_smoke.py` - Basic smoke tests to verify framework
|
||
|
|
- `tests/test_economy.py` - Economy system and building costs
|
||
|
|
- `tests/test_multiplayer.py` - Chat, cursor sync, building sync
|
||
|
|
- `tests/test_game_state.py` - Database persistence and validation
|
||
|
|
- `tests/test_integration.py` - End-to-end scenarios and stress tests
|
||
|
|
|
||
|
|
### Test Utilities
|
||
|
|
- `tests/test_client.py` - WebSocket client simulator
|
||
|
|
- `tests/__init__.py` - Test package initialization
|
||
|
|
- `run_tests.py` - Convenient test runner script
|
||
|
|
|
||
|
|
## Test Commands
|
||
|
|
|
||
|
|
### Basic Commands
|
||
|
|
```bash
|
||
|
|
# Run all tests
|
||
|
|
pytest
|
||
|
|
|
||
|
|
# Run with verbose output
|
||
|
|
pytest -v
|
||
|
|
|
||
|
|
# Run specific test file
|
||
|
|
pytest tests/test_economy.py
|
||
|
|
|
||
|
|
# Run specific test class
|
||
|
|
pytest tests/test_economy.py::TestEconomySystem
|
||
|
|
|
||
|
|
# Run specific test method
|
||
|
|
pytest tests/test_economy.py::TestEconomySystem::test_building_costs
|
||
|
|
|
||
|
|
# Run tests matching pattern
|
||
|
|
pytest -k "economy"
|
||
|
|
pytest -k "multiplayer"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Advanced Commands
|
||
|
|
```bash
|
||
|
|
# Run tests with coverage (install pytest-cov first)
|
||
|
|
pip install pytest-cov
|
||
|
|
pytest --cov=server --cov-report=html
|
||
|
|
|
||
|
|
# Run tests in parallel (install pytest-xdist first)
|
||
|
|
pip install pytest-xdist
|
||
|
|
pytest -n auto
|
||
|
|
|
||
|
|
# Run only failed tests from last run
|
||
|
|
pytest --lf
|
||
|
|
|
||
|
|
# Run tests and stop on first failure
|
||
|
|
pytest -x
|
||
|
|
```
|
||
|
|
|
||
|
|
## Test Categories
|
||
|
|
|
||
|
|
### 🏗️ Economy System Tests (`test_economy.py`)
|
||
|
|
|
||
|
|
**What it tests:**
|
||
|
|
- Building costs and money deduction
|
||
|
|
- Income/expense calculations per economic tick
|
||
|
|
- Road connectivity bonuses (5% per road in network)
|
||
|
|
- Population and power requirements
|
||
|
|
- Offline player processing (10% income rate)
|
||
|
|
|
||
|
|
**Key test methods:**
|
||
|
|
- `test_building_costs()` - Validates building price configurations
|
||
|
|
- `test_road_connectivity_bonus()` - Tests economy bonuses from road networks
|
||
|
|
- `test_offline_economy_processing()` - Verifies 10% income for offline players
|
||
|
|
- `test_population_requirements()` - Commercial building population requirements
|
||
|
|
|
||
|
|
### 👥 Multiplayer Tests (`test_multiplayer.py`)
|
||
|
|
|
||
|
|
**What it tests:**
|
||
|
|
- Real-time chat message broadcasting
|
||
|
|
- Cursor movement synchronization
|
||
|
|
- Building placement synchronization
|
||
|
|
- Player join/leave notifications
|
||
|
|
- Building ownership and permissions
|
||
|
|
|
||
|
|
**Key test methods:**
|
||
|
|
- `test_chat_message_broadcasting()` - Chat messages reach all players
|
||
|
|
- `test_building_placement_synchronization()` - Buildings sync across clients
|
||
|
|
- `test_building_ownership_permissions()` - Only owners can edit/delete buildings
|
||
|
|
- `test_rapid_chat_messages()` - Stress test for chat system
|
||
|
|
|
||
|
|
### 🏛️ Game State Tests (`test_game_state.py`)
|
||
|
|
|
||
|
|
**What it tests:**
|
||
|
|
- Player creation and management
|
||
|
|
- Building placement/removal validation
|
||
|
|
- Database save/load operations
|
||
|
|
- Road network connectivity algorithms
|
||
|
|
- Game state persistence across reconnections
|
||
|
|
|
||
|
|
**Key test methods:**
|
||
|
|
- `test_player_creation()` - Player data initialization
|
||
|
|
- `test_road_network_connectivity()` - Flood-fill algorithm for road zones
|
||
|
|
- `test_save_and_load_game_state()` - Database persistence
|
||
|
|
- `test_building_placement_validation()` - Validation rules
|
||
|
|
|
||
|
|
### 🎮 Integration Tests (`test_integration.py`)
|
||
|
|
|
||
|
|
**What it tests:**
|
||
|
|
- Complete multiplayer game scenarios
|
||
|
|
- End-to-end workflows
|
||
|
|
- System performance under load
|
||
|
|
- Error handling in complex scenarios
|
||
|
|
|
||
|
|
**Key test methods:**
|
||
|
|
- `test_two_player_city_building_session()` - Full multiplayer game
|
||
|
|
- `test_collaborative_city_with_chat()` - Players working together
|
||
|
|
- `test_rapid_building_placement()` - Stress test for building system
|
||
|
|
- `test_mixed_action_stress_test()` - Multiple action types simultaneously
|
||
|
|
|
||
|
|
### 💨 Smoke Tests (`test_smoke.py`)
|
||
|
|
|
||
|
|
**What it tests:**
|
||
|
|
- Basic framework functionality
|
||
|
|
- WebSocket connection establishment
|
||
|
|
- Simple building placement
|
||
|
|
- Basic chat functionality
|
||
|
|
|
||
|
|
## Test Client Architecture
|
||
|
|
|
||
|
|
### TestWebSocketClient
|
||
|
|
|
||
|
|
The `TestWebSocketClient` simulates a real game client:
|
||
|
|
|
||
|
|
```python
|
||
|
|
from tests.test_client import TestWebSocketClient
|
||
|
|
|
||
|
|
# Create client
|
||
|
|
client = TestWebSocketClient("player_nickname")
|
||
|
|
await client.connect()
|
||
|
|
|
||
|
|
# Game actions
|
||
|
|
await client.place_building("small_house", 5, 5)
|
||
|
|
await client.send_chat("Hello world!")
|
||
|
|
await client.send_cursor_move(10, 15)
|
||
|
|
|
||
|
|
# Receive server messages
|
||
|
|
message = await client.receive_message(timeout=2.0)
|
||
|
|
|
||
|
|
# Clean up
|
||
|
|
await client.disconnect()
|
||
|
|
```
|
||
|
|
|
||
|
|
### Context Manager Pattern
|
||
|
|
|
||
|
|
For easier test management:
|
||
|
|
|
||
|
|
```python
|
||
|
|
from tests.test_client import test_clients
|
||
|
|
|
||
|
|
# Single client
|
||
|
|
async with test_clients("player1") as [client]:
|
||
|
|
await client.place_building("road", 0, 0)
|
||
|
|
|
||
|
|
# Multiple clients
|
||
|
|
async with test_clients("alice", "bob", "charlie") as [alice, bob, charlie]:
|
||
|
|
await alice.send_chat("Let's build together!")
|
||
|
|
# All clients automatically disconnected at end
|
||
|
|
```
|
||
|
|
|
||
|
|
## Test Data Management
|
||
|
|
|
||
|
|
### Database Isolation
|
||
|
|
- Tests use temporary databases to avoid affecting game data
|
||
|
|
- Each test gets a fresh database state
|
||
|
|
- Database fixtures handle setup and teardown automatically
|
||
|
|
|
||
|
|
### Test Cleanup
|
||
|
|
- WebSocket connections are automatically closed
|
||
|
|
- Temporary files are cleaned up
|
||
|
|
- No test data persists between runs
|
||
|
|
|
||
|
|
## Writing New Tests
|
||
|
|
|
||
|
|
### Adding Economy Tests
|
||
|
|
|
||
|
|
```python
|
||
|
|
def test_new_building_feature(self, game_setup):
|
||
|
|
"""Test a new building feature"""
|
||
|
|
game_state, economy_engine, player = game_setup
|
||
|
|
|
||
|
|
# Test the new feature
|
||
|
|
result = game_state.place_building("player_id", "new_building", 0, 0)
|
||
|
|
assert result["success"] == True
|
||
|
|
|
||
|
|
# Test economy impact
|
||
|
|
economy_engine.tick()
|
||
|
|
# Add assertions...
|
||
|
|
```
|
||
|
|
|
||
|
|
### Adding Multiplayer Tests
|
||
|
|
|
||
|
|
```python
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_new_multiplayer_feature(self):
|
||
|
|
"""Test a new multiplayer feature"""
|
||
|
|
async with test_clients("player1", "player2") as [p1, p2]:
|
||
|
|
# Test the interaction
|
||
|
|
await p1.some_new_action()
|
||
|
|
|
||
|
|
# Verify p2 receives update
|
||
|
|
message = await p2.receive_message(timeout=2.0)
|
||
|
|
assert message["type"] == "expected_message_type"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Test Patterns
|
||
|
|
|
||
|
|
**Async Tests:**
|
||
|
|
```python
|
||
|
|
@pytest.mark.asyncio
|
||
|
|
async def test_async_functionality(self):
|
||
|
|
# Use async/await for WebSocket interactions
|
||
|
|
pass
|
||
|
|
```
|
||
|
|
|
||
|
|
**Fixtures:**
|
||
|
|
```python
|
||
|
|
@pytest.fixture
|
||
|
|
def game_setup(self):
|
||
|
|
# Setup test data
|
||
|
|
return game_state, economy_engine, player
|
||
|
|
```
|
||
|
|
|
||
|
|
**Timeouts:**
|
||
|
|
```python
|
||
|
|
# Always use timeouts for WebSocket operations
|
||
|
|
message = await client.receive_message(timeout=2.0)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting Tests
|
||
|
|
|
||
|
|
### Common Issues
|
||
|
|
|
||
|
|
**Server not running:**
|
||
|
|
```
|
||
|
|
⚠️ Game server is not running on http://127.0.0.1:9901
|
||
|
|
```
|
||
|
|
**Solution:** Start the server with `python run.py`
|
||
|
|
|
||
|
|
**WebSocket connection failures:**
|
||
|
|
- Check if server is running on correct port (9901)
|
||
|
|
- Ensure no firewall blocking localhost connections
|
||
|
|
- Try restarting the server
|
||
|
|
|
||
|
|
**Test timeouts:**
|
||
|
|
- Economy tests may be slow due to 10-second ticks
|
||
|
|
- Increase timeouts for integration tests
|
||
|
|
- Check server logs for errors
|
||
|
|
|
||
|
|
**Database errors:**
|
||
|
|
- Tests should use isolated temporary databases
|
||
|
|
- If tests interfere, check fixture cleanup
|
||
|
|
|
||
|
|
### Debug Mode
|
||
|
|
|
||
|
|
Run tests with more verbose output:
|
||
|
|
```bash
|
||
|
|
# Maximum verbosity
|
||
|
|
pytest -vvv
|
||
|
|
|
||
|
|
# Show stdout/stderr
|
||
|
|
pytest -s
|
||
|
|
|
||
|
|
# Drop into debugger on failure
|
||
|
|
pytest --pdb
|
||
|
|
```
|
||
|
|
|
||
|
|
## Continuous Integration
|
||
|
|
|
||
|
|
The test suite is designed to be CI-friendly:
|
||
|
|
|
||
|
|
- All tests are deterministic
|
||
|
|
- No external dependencies except the local server
|
||
|
|
- Isolated database usage
|
||
|
|
- Reasonable timeouts
|
||
|
|
- Clear pass/fail criteria
|
||
|
|
|
||
|
|
### CI Setup Example
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# Example GitHub Actions workflow
|
||
|
|
- name: Install dependencies
|
||
|
|
run: pip install -r requirements.txt
|
||
|
|
|
||
|
|
- name: Start game server
|
||
|
|
run: python run.py &
|
||
|
|
|
||
|
|
- name: Wait for server
|
||
|
|
run: sleep 5
|
||
|
|
|
||
|
|
- name: Run tests
|
||
|
|
run: pytest -v
|
||
|
|
```
|
||
|
|
|
||
|
|
## Performance Benchmarks
|
||
|
|
|
||
|
|
The test suite includes stress tests that validate:
|
||
|
|
|
||
|
|
- **Building placement:** 20+ simultaneous actions
|
||
|
|
- **Chat messages:** 30+ rapid messages
|
||
|
|
- **Multiple players:** 4+ concurrent clients
|
||
|
|
- **Mixed actions:** Building + chat + cursor movement
|
||
|
|
|
||
|
|
These benchmarks help ensure the game performs well under realistic load.
|
||
|
|
|
||
|
|
## Test Coverage Goals
|
||
|
|
|
||
|
|
- **WebSocket API:** 100% coverage of all message types
|
||
|
|
- **Economy system:** All building types and calculation paths
|
||
|
|
- **Game state:** All validation rules and edge cases
|
||
|
|
- **Database:** Save/load operations for all data types
|
||
|
|
- **Multiplayer:** All synchronization scenarios
|
||
|
|
|
||
|
|
Run coverage analysis with:
|
||
|
|
```bash
|
||
|
|
pip install pytest-cov
|
||
|
|
pytest --cov=server --cov-report=html
|
||
|
|
open htmlcov/index.html # View coverage report
|
||
|
|
```
|