Skip to content
This repository was archived by the owner on Mar 11, 2026. It is now read-only.

Commit 68d89cc

Browse files
author
astrbot-docs-agent[bot]
committed
docs: update for AstrBotDevs/AstrBot#5354
1 parent 7f682f5 commit 68d89cc

File tree

3 files changed

+576
-0
lines changed

3 files changed

+576
-0
lines changed

.vitepress/config.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ export default defineConfig({
197197
{ text: "文转图", link: "/guides/html-to-pic" },
198198
{ text: "会话控制器", link: "/guides/session-control" },
199199
{ text: "杂项", link: "/guides/other" },
200+
{ text: "插件测试", link: "/guides/testing" },
200201
{ text: "发布插件", link: "/plugin-publish" },
201202
{ text: "插件指南(旧)", link: "/plugin" },
202203
],
@@ -424,6 +425,7 @@ export default defineConfig({
424425
{ text: "Storage", link: "/guides/storage" },
425426
{ text: "HTML to Image", link: "/guides/html-to-pic" },
426427
{ text: "Session Control", link: "/guides/session-control" },
428+
{ text: "Plugin Testing", link: "/guides/testing" },
427429
{ text: "Publish Plugin", link: "/plugin-publish" },
428430
],
429431
},

en/dev/star/guides/testing.md

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
# Plugin Testing
2+
3+
AstrBot provides a comprehensive testing framework to help you write unit tests and integration tests for your plugins.
4+
5+
## Testing Framework Overview
6+
7+
AstrBot's testing framework is located in the `tests/` directory and includes the following components:
8+
9+
- `conftest.py` - Shared pytest fixtures and test configuration
10+
- `fixtures/` - Test data and helper utilities
11+
- `mocks/` - Platform adapter mock tools
12+
- `helpers.py` - Test helper functions
13+
- `configs/` - Test configuration files
14+
- `messages/` - Test message data
15+
16+
## Quick Start
17+
18+
### Install Test Dependencies
19+
20+
```bash
21+
pip install pytest pytest-asyncio pytest-cov
22+
```
23+
24+
### Write Your First Test
25+
26+
Create a `tests/` folder in your plugin directory and add a test file:
27+
28+
```python
29+
# tests/test_my_plugin.py
30+
import pytest
31+
from astrbot.api.event import AstrMessageEvent
32+
33+
class TestMyPlugin:
34+
def test_example(self):
35+
"""A simple test example"""
36+
assert True
37+
```
38+
39+
Run the tests:
40+
41+
```bash
42+
pytest tests/
43+
```
44+
45+
## Using Fixtures
46+
47+
AstrBot provides various predefined fixtures that can be used directly in tests:
48+
49+
### Basic Fixtures
50+
51+
```python
52+
def test_with_temp_dir(temp_dir):
53+
"""Use temporary directory"""
54+
assert temp_dir.exists()
55+
56+
def test_with_temp_config(temp_config_file):
57+
"""Use temporary config file"""
58+
assert temp_config_file.exists()
59+
```
60+
61+
### Mock Fixtures
62+
63+
```python
64+
def test_with_mock_provider(mock_provider):
65+
"""Use mock Provider"""
66+
assert mock_provider.get_model() == "gpt-4o-mini"
67+
68+
def test_with_mock_platform(mock_platform):
69+
"""Use mock Platform"""
70+
assert mock_platform.platform_name == "test_platform"
71+
72+
def test_with_mock_event(mock_event):
73+
"""Use mock message event"""
74+
assert mock_event.message_str == "Hello, world!"
75+
```
76+
77+
### Database Fixtures
78+
79+
```python
80+
@pytest.mark.asyncio
81+
async def test_with_temp_db(temp_db):
82+
"""Use temporary database"""
83+
# temp_db is an async fixture
84+
assert temp_db is not None
85+
```
86+
87+
## Platform Adapter Mocks
88+
89+
AstrBot provides mock tools for platform adapters to facilitate cross-platform testing:
90+
91+
### Telegram Mock
92+
93+
```python
94+
from tests.fixtures.mocks import MockTelegramBuilder, mock_telegram_modules
95+
96+
def test_telegram_adapter():
97+
"""Test Telegram adapter"""
98+
bot = MockTelegramBuilder.create_bot()
99+
assert bot is not None
100+
```
101+
102+
### Discord Mock
103+
104+
```python
105+
from tests.fixtures.mocks import MockDiscordBuilder, mock_discord_modules
106+
107+
def test_discord_adapter():
108+
"""Test Discord adapter"""
109+
client = MockDiscordBuilder.create_client()
110+
assert client is not None
111+
```
112+
113+
### Aiocqhttp Mock
114+
115+
```python
116+
from tests.fixtures.mocks import MockAiocqhttpBuilder, mock_aiocqhttp_modules
117+
118+
def test_aiocqhttp_adapter():
119+
"""Test Aiocqhttp adapter"""
120+
bot = MockAiocqhttpBuilder.create_bot()
121+
assert bot is not None
122+
```
123+
124+
## Test Helper Functions
125+
126+
### Creating Platform Configs
127+
128+
```python
129+
from tests.fixtures import make_platform_config
130+
131+
def test_platform_config():
132+
"""Create platform config"""
133+
telegram_config = make_platform_config("telegram")
134+
assert telegram_config["id"] == "test_telegram"
135+
136+
# Custom config
137+
custom_config = make_platform_config("discord", discord_token="my_token")
138+
assert custom_config["discord_token"] == "my_token"
139+
```
140+
141+
### Creating Mock Message Components
142+
143+
```python
144+
from tests.fixtures import create_mock_message_component
145+
146+
def test_message_component():
147+
"""Create message components"""
148+
plain = create_mock_message_component("plain", text="Hello")
149+
image = create_mock_message_component("image", url="https://example.com/img.jpg")
150+
```
151+
152+
### Creating Mock LLM Responses
153+
154+
```python
155+
from tests.fixtures import create_mock_llm_response
156+
157+
def test_llm_response():
158+
"""Create LLM response"""
159+
response = create_mock_llm_response(
160+
completion_text="Hello! How can I help you?",
161+
role="assistant"
162+
)
163+
assert response.completion_text == "Hello! How can I help you?"
164+
```
165+
166+
## Test Markers
167+
168+
AstrBot uses pytest markers to categorize tests:
169+
170+
```python
171+
import pytest
172+
173+
@pytest.mark.unit
174+
def test_unit():
175+
"""Unit test"""
176+
pass
177+
178+
@pytest.mark.integration
179+
def test_integration():
180+
"""Integration test"""
181+
pass
182+
183+
@pytest.mark.slow
184+
def test_slow():
185+
"""Slow test"""
186+
pass
187+
188+
@pytest.mark.platform("telegram")
189+
def test_telegram_specific():
190+
"""Telegram platform specific test"""
191+
pass
192+
193+
@pytest.mark.provider
194+
def test_provider():
195+
"""Provider test (requires API Key)"""
196+
pass
197+
```
198+
199+
## Test Configuration File
200+
201+
### pytest.ini
202+
203+
Create `pytest.ini` in your project root:
204+
205+
```ini
206+
[pytest]
207+
asyncio_mode = auto
208+
testpaths = tests
209+
python_files = test_*.py
210+
python_classes = Test*
211+
python_functions = test_*
212+
markers =
213+
unit: Unit tests
214+
integration: Integration tests
215+
slow: Slow tests
216+
platform: Platform adapter tests
217+
provider: LLM Provider tests
218+
```
219+
220+
## Running Tests
221+
222+
### Run All Tests
223+
224+
```bash
225+
pytest
226+
```
227+
228+
### Run Specific Tests
229+
230+
```bash
231+
# Run a single file
232+
pytest tests/test_my_plugin.py
233+
234+
# Run a single test function
235+
pytest tests/test_my_plugin.py::test_example
236+
237+
# Run tests with specific markers
238+
pytest -m unit
239+
pytest -m "not slow"
240+
```
241+
242+
### Test Coverage
243+
244+
```bash
245+
pytest --cov=astrbot --cov-report=html
246+
```
247+
248+
## Best Practices
249+
250+
1. **Test Isolation**: Each test should run independently without depending on other tests' state
251+
2. **Use Fixtures**: Leverage AstrBot's fixtures to reduce code duplication
252+
3. **Mock External Dependencies**: Use mock tools to simulate external services and avoid real API calls
253+
4. **Clear Test Names**: Test function names should clearly describe what is being tested
254+
5. **Test Edge Cases**: Test not only the happy path but also error conditions
255+
256+
## Example: Testing Plugin Commands
257+
258+
```python
259+
import pytest
260+
from astrbot.api.event import AstrMessageEvent
261+
from astrbot.api.star import Context, Star
262+
263+
class TestMyPlugin:
264+
@pytest.fixture
265+
def plugin(self, mock_context):
266+
"""Create plugin instance"""
267+
from my_plugin import MyPlugin
268+
return MyPlugin(mock_context)
269+
270+
@pytest.mark.asyncio
271+
async def test_helloworld_command(self, plugin, mock_event):
272+
"""Test helloworld command"""
273+
# Set message content
274+
mock_event.message_str = "/helloworld"
275+
276+
# Call command handler
277+
result = await plugin.helloworld(mock_event)
278+
279+
# Verify result
280+
assert result is not None
281+
```
282+
283+
## Related Resources
284+
285+
- [pytest Official Documentation](https://docs.pytest.org/)
286+
- [pytest-asyncio Documentation](https://pytest-asyncio.readthedocs.io/)
287+
- [unittest.mock Documentation](https://docs.python.org/3/library/unittest.mock.html)

0 commit comments

Comments
 (0)