Unit Testing
Relevant source files
The following files were used as context for generating this wiki page:
Unit testing in the mt5linux project is designed to validate individual components, utility functions, and data models in isolation. These tests are strictly pure unit tests, meaning they do not require a live MetaTrader 5 terminal, Docker containers, or gRPC server connectivity. Instead, they rely on mocking, temporary in-memory databases, and property-based testing to ensure technical accuracy and regressions safety.
The suite utilizes pytest as the primary test runner, with Hypothesis for property-based testing and Pydantic for schema validation. The scope includes verifying exception handling, data transformations, Write-Ahead Log (WAL) transitions, and the internal logic of the transaction orchestrator.
Sources: tests/unit/test_utilities_unit.py:12-19, pyproject.toml:98-106
Test Architecture and Configuration
The unit testing framework is configured via pyproject.toml, defining markers for different test types and setting up the execution environment.
Testing Framework Components
| Component | Usage in Project |
|---|---|
| Pytest | Core test runner and fixture provider. |
| Hypothesis | Property-based testing for edge case discovery. |
| Pydantic | Validation of model schemas and data integrity. |
| aiosqlite | Used for testing the WAL persistence layer with temporary databases. |
| Mocks/AsyncMocks | Simulating gRPC calls and MT5 terminal responses. |
Sources: pyproject.toml:31-41, tests/unit/test_model_schema_validation.py:16-18, tests/unit/test_wal.py:18-24
Test Organization
The project organizes unit tests into a specific directory structure under tests/unit/.
The diagram above shows the logical separation of test concerns within the unit testing package.
Sources: tests/unit/init.py:1, tests/unit/test_utilities_unit.py:1-10
Model and Schema Validation
Model testing ensures that Pydantic classes represent MetaTrader 5 data structures accurately. These tests validate required fields, data types, and the immutability of models.
Validation Rules
Tests enforce that specific models must fail if required fields are missing or if frozen (immutable) models are modified post-instantiation.
| Model | Primary Validated Fields | Immutability |
|---|---|---|
| AccountInfo | login, balance, equity, currency | Frozen |
| SymbolInfo | name, bid, ask, volume_min | Frozen |
| Tick | time, bid, ask, volume | Frozen |
| OrderRequest | action, symbol, volume, type | Mutable |
| OrderResult | retcode, deal, order | Frozen |
Sources: tests/unit/test_model_schema_validation.py:34-106, tests/unit/test_model_schema_validation.py:189-224
Factory Methods
Unit tests verify the from_mt5() factory method, which converts various inputs (dicts, namedtuples, or objects) into canonical Pydantic models. This handles edge cases like None inputs, where OrderResult returns a synthetic error instead of None to maintain system stability.
Sources: tests/unit/test_model_schema_validation.py:141-182
Utility and Logic Testing
The utility test suite covers low-level data transformations and high-level transaction orchestration.
Data Transformations and Introspection
Tests verify that MT5Utilities.Data correctly handles:
- Wrapper: Exposing dictionary keys as attributes.
- Numpy/Proto: Reassembling chunked gRPC responses and converting raw bytes into structured numpy arrays using specific dtypes.
- Introspection: Discovering tuple field orders for complex MT5 objects using
__match_args__or member descriptors.
Sources: tests/unit/test_utilities_unit.py:108-144, tests/unit/test_utilities_unit.py:270-345
Transaction Orchestration Logic
The TransactionOrchestrator is tested using a dependency injection pattern with AsyncMock.
This diagram illustrates the unit test for a timed-out gRPC call that is subsequently recovered via state verification.
Sources: tests/unit/test_utilities_unit.py:464-500
Write-Ahead Log (WAL) Testing
The WAL unit tests verify the persistence of order operations using a real SQLite database in a temporary directory to ensure transaction safety without mocks for the database layer.
Status Transitions
Tests track the request lifecycle through specific status values defined in MT5Constants.
| Status | Integer Value | Description |
|---|---|---|
| PENDING | 0 | Intent logged, not yet sent. |
| SENT | 1 | Request dispatched via gRPC. |
| VERIFIED | 2 | Confirmed execution by MT5. |
| FAILED | 3 | Permanent failure recorded. |
Sources: tests/unit/test_wal.py:202-212, tests/unit/test_wal.py:72-108
Lifecycle and Recovery
Tests ensure the database schema is created idempotently and that the system can recover "incomplete" entries (PENDING or SENT) after a crash, ordered by timestamp.
Sources: tests/unit/test_wal.py:53-70, tests/unit/test_wal.py:140-160
Property-Based Testing Integration
The project uses Hypothesis within its test suite (primarily in integration but defined in constants) to generate a wide range of inputs for volume, deviation, and bar counts. This approach identifies edge cases that manual test cases might miss.
# Constants defining generation bounds used in property tests
class Generators:
VOLUME_MIN: Final[float] = 0.01
VOLUME_MAX: Final[float] = 10.0
DEVIATION_MIN: Final[int] = 1
BAR_COUNT_VALID_MAX: Final[int] = 10000
Sources: tests/constants.py:456-476, pyproject.toml:31
Unit testing in mt5linux provides a robust foundation by verifying data integrity, persistence state machines, and complex retry logic. By maintaining a strict "no-server" policy for unit tests, the project ensures fast execution and reliable validation of core business logic.
Sources: tests/unit/test_utilities_unit.py:12-19, tests/unit/test_model_schema_validation.py:12-21