Testing
Ash uses pytest with async support for testing.
Running Tests
Run all tests:
uv run pytestRun with verbose output:
uv run pytest -vRun specific test file:
uv run pytest tests/unit/test_config.pyRun specific test:
uv run pytest tests/unit/test_config.py::test_load_configTest Organization
tests/├── conftest.py # Shared fixtures├── unit/ # Unit tests│ ├── test_config.py│ ├── test_llm.py│ ├── test_memory.py│ └── test_tools.py└── integration/ # Integration tests └── test_agent.pyWriting Tests
Async Tests
Use pytest-asyncio:
import pytest
@pytest.mark.asyncioasync def test_async_function(): result = await some_async_function() assert result == expectedFixtures
Common fixtures in conftest.py:
import pytestfrom ash.config import AshConfig
@pytest.fixturedef config(): return AshConfig( models={"default": {...}}, )
@pytest.fixtureasync def memory_store(config): store = MemoryStore(config) await store.connect() yield store await store.close()Mocking
Mock external services:
from unittest.mock import AsyncMock, patch
@pytest.mark.asyncioasync def test_with_mock(): with patch("ash.llm.anthropic.AsyncAnthropic") as mock: mock.return_value.messages.create = AsyncMock( return_value=mock_response ) result = await provider.complete(messages) assert result.content == expectedCoverage
Run with coverage:
uv run pytest --cov=ash --cov-report=htmlView report at htmlcov/index.html.
Test Categories
Unit Tests
Test individual components in isolation:
def test_config_validation(): config = AshConfig(models={...}) assert config.default_model.provider == "anthropic"Integration Tests
Test component interactions:
@pytest.mark.asyncioasync def test_agent_tool_execution(): agent = Agent(config, tools) response = await agent.process("run ls") assert "file.txt" in responseCI Integration
Tests run on every PR via GitHub Actions:
- run: uv run pytest --cov-report=xml- uses: codecov/codecov-action@v4