Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.
Add unit tests to AReaL following the project's testing conventions.
This skill is triggered when:
AReaL has two main test categories:
| Test Type | Purpose | Location Pattern | How It Runs |
|---|---|---|---|
| Unit Tests | Test individual functions/modules | tests/test_<module>_<feature>.py |
Directly via pytest |
| Distributed Tests | Test distributed/parallel behavior | tests/torchrun/run_*.py |
Via torchrun (called by pytest subprocess) |
Note: All tests are invoked via pytest. Distributed tests use torchrun but are
still called from pytest test files.
Create test file with naming convention: test_<module>_<feature>.py
import pytest
import torch
# Import the module to test
from areal.dataset.gsm8k import get_gsm8k_sft_dataset
from tests.utils import get_dataset_path # Optional test utilities
# For mocking tokenizer: from unittest.mock import MagicMock
Follow Arrange-Act-Assert pattern:
def test_function_under_condition_returns_expected():
"""Test that function returns expected value under condition."""
# Arrange
input_data = 5
expected_output = 10
# Act
result = function_under_test(input_data)
# Assert
assert result == expected_output
Use appropriate pytest markers:
| Marker | When to Use |
|---|---|
@pytest.mark.slow |
Test takes > 10 seconds (excluded from CI by default) |
@pytest.mark.ci |
Slow test that must run in CI (use with @pytest.mark.slow) |
@pytest.mark.asyncio |
Async test functions |
@pytest.mark.skipif(cond, reason=...) |
Conditional skip |
@pytest.mark.parametrize(...) |
Parameterized tests |
CI Test Strategy:
@pytest.mark.slow: Excluded from CI by default (CI runs pytest -m "not slow")@pytest.mark.slow + @pytest.mark.ci: Slow but must run in CI@pytest.mark.asyncio
async def test_async_function():
result = await async_function()
assert result == expected
@pytest.mark.skipif(not torch.cuda.is_available(), reason="CUDA not available")
def test_gpu_feature():
tensor = torch.tensor([1, 2, 3], device="cuda")
# ... assertions
@pytest.mark.parametrize("batch_size", [1, 4, 16])
def test_with_parameters(batch_size):
# Parameterized test
@pytest.mark.slow
def test_slow_function():
# Excluded from CI by default
@pytest.mark.slow
@pytest.mark.ci
def test_slow_but_required_in_ci():
# Slow but must run in CI
For unit tests that need distributed mocks:
import torch.distributed as dist
def test_distributed_function(monkeypatch):
monkeypatch.setattr(dist, "get_rank", lambda: 0)
monkeypatch.setattr(dist, "get_world_size", lambda: 2)
result = distributed_function()
assert result == expected
Always skip gracefully when GPU unavailable:
CUDA_AVAILABLE = torch.cuda.is_available()
@pytest.mark.skipif(not CUDA_AVAILABLE, reason="CUDA not available")
def test_gpu_function():
tensor = torch.tensor([1, 2, 3], device="cuda")
# ... assertions
torch.distributed.fake_pg for unit testsdist.get_rank() and dist.get_world_size() explicitlytorch.cuda.empty_cache() in fixturestorch.testing.assert_close() for tensor comparisonrtol/atol explicitly for numerical testsassert tensor.equal() - no useful error message| Test File | Description | Key Patterns |
|---|---|---|
tests/test_utils.py |
Utility function tests | Fixtures, parametrized tests |
tests/test_examples.py |
Integration tests with dataset loading | Dataset path resolution, success pattern matching |
tests/test_fsdp_engine_nccl.py |
Distributed tests | Torchrun integration |
test_*.py naming@pytest.mark.skipif for GPU teststorch.testing.assert_close() not
assert tensor.equal()torch.cuda.empty_cache()test_<what>_<condition>_<expected> patternThis skill complements other AReaL development skills:
/add-dataset: Add tests for new dataset loaders/add-workflow: Add tests for new workflows/add-reward: Add tests for new reward functionsplanner agent: Reference this skill when planning test implementation# First check GPU availability (many tests require GPU)
python -c "import torch; print('GPU available:', torch.cuda.is_available())"
# Run specific test file
uv run pytest tests/test_<name>.py
# Skip slow tests (CI default)
uv run pytest -m "not slow"
# Run with verbose output
uv run pytest -v
# Run distributed tests (requires torchrun and multi-GPU)
# Note: Usually invoked via pytest test files
torchrun --nproc_per_node=2 tests/torchrun/run_<test>.py