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):

    python run.py
    
  2. 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 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

# 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 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:

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