测试
The test suite is divided into broad categories of tests including:
- Unit tests
- Integration tests
- Acceptance tests
- 性能 tests
- 内存 leak tests
The 性能 tests exist to aid 开发 of 性能-critical components.
Tests can be run using pytest, which is our primary test runner.
We recommend using parametrized tests and fixtures (e.g., @pytest.mark.parametrize
) to avoid repetitive code and improve clarity.
Running tests
Python tests
From the repository root:
make pytest
# or
uv run --active --no-sync pytest --new-first --failed-first
# or simply
pytest
For 性能 tests:
make test-performance
# or
uv run --active --no-sync pytest tests/performance_tests --benchmark-disable-gc --codspeed
Rust tests
make cargo-test
# or
cargo nextest run --workspace --features "python,ffi,high-precision,defi" --cargo-profile nextest
IDE integration
- PyCharm: Right-click on tests folder or file → "Run pytest"
- VS Code: Use the Python Test Explorer extension
Test style
- Test function naming should be descriptive of what is under test; it is not necessary to include the expected assertions in the function name.
- Test functions may have docstrings that can be useful for elaborating on test 设置, scenarios, and expected assertions.
- Prefer pytest style free functions for Python tests over test classes with 设置 methods.
- Group assertions where possible - perform all 设置/act steps first, then assert expectations together at the end of the test to avoid the act-assert-act smell.
- Using
unwrap
,expect
, or directpanic!
/assert
calls inside tests is acceptable. The clarity and conciseness of the test suite outweigh defensive error-handling that is required in 生产 code.
Mocks
Unit tests will often include other components acting as mocks. The intent of this is to simplify
the test suite to avoid extensive use of a mocking 框架, although MagicMock
objects are
currently used in particular cases.
Code coverage
Code coverage output is generated using coverage
and reported using codecov.
High test coverage is a goal for the project, however not at the expense of appropriate 错误处理 or causing "test induced damage" to the 架构.
There are currently areas of the codebase which are impossible to test unless there is a change to the 生产 code. For example, the last condition check of an if-else block which would catch an unrecognized value; these should be left in place in case there is a change to the 生产 code which these checks could then catch.
Other design-time exceptions may also be impossible to test for, and so 100% test coverage is not the ultimate goal.
Excluded code coverage
The pragma: no cover
comments found throughout the codebase exclude code from test coverage.
The reason for their use is to reduce redundant/needless tests just to keep coverage high, such as:
- Asserting an abstract method raises
NotImplementedError
when called. - Asserting the final condition check of an if-else block when impossible to test (as above).
These tests are expensive to maintain (as they must be kept in line with any refactorings) and offer little to no benefit in return.
The intention is for all abstract method implementations to be fully covered by tests.
Therefore pragma: no cover
should be judiciously removed when no longer appropriate, and its use restricted to the above cases.
调试 Rust tests
Rust tests can be debugged using the default test 配置.
If you want to run all tests while compiling with debug symbols for later 调试 some tests individually,
run make cargo-test-debug
instead of make cargo-test
.
In IntelliJ IDEA, to debug parametrised tests starting with #[rstest]
with arguments defined in the header of the test,
you need to modify the run 配置 of the test so it looks like test --package nautilus-model --lib 数据::bar::tests::test_get_time_bar_start::case_1
(remove -- --exact
at the end of the string and append ::case_n
where n
is an integer corresponding to the n-th parametrised test starting at 1).
The reason for this is documented here (the test is expanded into a module with several functions named case_n
).
In VS Code, it is possible to directly select which test case to debug.
Python + Rust mixed 调试 指南
This approach allows to debug both Python and Rust code simultaneously from a Jupyter notebook inside VS Code.
设置
Install VS Code extensions: Rust Analyzer, CodeLLDB, Python, Jupyter
Step 0: Compile nautilus_trader with debug symbols
cd nautilus_trader && make build-debug-pyo3
Step 1: Setup Debugging Configuration
from nautilus_trader.test_kit.debug_helpers import setup_debugging
setup_debugging()
This creates the necessary VS Code 调试 configurations and starts a debugpy 服务器 the Python debugger can connect to.
Note: by default the .vscode folder containing the debugging configurations
is assumed to be one folder above the nautilus_trader
root directory.
You can adjust this if needed.
Step 2: Set Breakpoints
- Python breakpoints: Set in VS Code in the Python source files.
- Rust breakpoints: Set in VS Code in the Rust source files.
Step 3: Start Mixed Debugging
- In VS Code: Select "Debug Jupyter + Rust (Mixed)" configuration.
- Start 调试 (F5) or press the right arrow green button.
- Both Python and Rust debuggers will attach to your Jupyter session.
Step 4: Execute Code
Run your Jupyter notebook cells that call Rust functions. The debugger will stop at breakpoints in both Python and Rust code.
Available Configurations
setup_debugging()
creates these VS Code configurations:
Debug Jupyter + Rust (Mixed)
- Mixed 调试 for Jupyter notebooks.Jupyter Mixed 调试 (Python)
- Python-only 调试 for notebooks.Rust Debugger (for jupyter 调试)
- Rust-only 调试 for notebooks.
Example
Open and run the example notebook: debug_mixed_jupyter.ipynb