Testing Guide - City Builder Game
This document provides comprehensive information about the testing framework for the City Builder multiplayer game.
Quick Start
Prerequisites
-
Start the game server (required for tests):
python run.py -
Run tests (in another terminal):
# 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 frameworktests/test_economy.py- Economy system and building coststests/test_multiplayer.py- Chat, cursor sync, building synctests/test_game_state.py- Database persistence and validationtests/test_integration.py- End-to-end scenarios and stress tests
Test Utilities
tests/test_client.py- WebSocket client simulatortests/__init__.py- Test package initializationrun_tests.py- Convenient test runner script
Test Commands
Basic Commands
# 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
# 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 configurationstest_road_connectivity_bonus()- Tests economy bonuses from road networkstest_offline_economy_processing()- Verifies 10% income for offline playerstest_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 playerstest_building_placement_synchronization()- Buildings sync across clientstest_building_ownership_permissions()- Only owners can edit/delete buildingstest_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 initializationtest_road_network_connectivity()- Flood-fill algorithm for road zonestest_save_and_load_game_state()- Database persistencetest_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 gametest_collaborative_city_with_chat()- Players working togethertest_rapid_building_placement()- Stress test for building systemtest_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:
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:
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
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
@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:
@pytest.mark.asyncio
async def test_async_functionality(self):
# Use async/await for WebSocket interactions
pass
Fixtures:
@pytest.fixture
def game_setup(self):
# Setup test data
return game_state, economy_engine, player
Timeouts:
# 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:
# 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
# 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:
pip install pytest-cov
pytest --cov=server --cov-report=html
open htmlcov/index.html # View coverage report