### Example conftest.py for NoneBug Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md A complete conftest.py file demonstrating NoneBug setup, including initializing NoneBot with custom configurations and managing lifespan hooks. ```python import pytest import nonebot from nonebug import NONEBOT_INIT_KWARGS, NONEBOT_START_LIFESPAN def pytest_configure(config: pytest.Config): """Configure NoneBug for tests.""" # Initialize NoneBot with custom config config.stash[NONEBOT_INIT_KWARGS] = { "nickname": "TestBot", "debug": True, } # Run startup/shutdown hooks (default is True) config.stash[NONEBOT_START_LIFESPAN] = True @pytest.fixture(scope="session", autouse=True) def setup_test_adapters(): """Register test adapters if needed.""" driver = nonebot.get_driver() # Register adapters here if necessary yield ``` -------------------------------- ### Define HTTP Server Setup for Adapter Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md Example of how an adapter can set up an HTTP server handler using HTTPServerSetup. This defines the URL, method, endpoint name, and handler function for an HTTP request. ```python class MyAdapter(Adapter): def __init__(self, driver: Driver, **kwargs): super().__init__(driver, **kwargs) setup = HTTPServerSetup( URL("/webhook"), "POST", "webhook", self.handle_webhook ) self.setup_http_server(setup) async def handle_webhook(self, request: Request) -> Response: # Process webhook return Response(200, content="OK") ``` -------------------------------- ### Install NoneBug with pip Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Install NoneBug using pip, optionally with the anyio backend. ```bash pip install nonebug anyio ``` -------------------------------- ### Context Lifecycle Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Provides a complete lifecycle example of a testing context, from creation and entry to execution and exit, including resource registration and expectation validation. ```python async def test_example(app: App): # Create context (not entered yet) ctx = app.test_api() # Enter context async with ctx: # __aenter__ is called # setup() runs here adapter = ctx.create_adapter() # Resources registered ctx.should_call_api("test", {}) # Code runs here # run() is called on exit # After exiting: # - run() validated expectations # - Stack cleaned up all resources # - app.context is None ``` -------------------------------- ### Install NoneBug with pip Source: https://github.com/nonebot/nonebug/blob/master/README.md Install NoneBug and pytest-asyncio using pip. Alternatively, use anyio. ```bash pip install nonebug pytest-asyncio # 或者使用 anyio pip install nonebug anyio ``` -------------------------------- ### Context Setup Hook Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Override the `setup()` method in subclasses to initialize resources, patch methods, or configure the test environment before execution. ```python async def setup(self) -> None: pass ``` -------------------------------- ### Install NoneBug with uv Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Installs NoneBug and pytest-asyncio using uv, with an option to include anyio backend. ```bash uv add nonebug pytest-asyncio --group test # or with anyio backend uv add nonebug anyio --group test ``` -------------------------------- ### Install NoneBug with pip Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Installs NoneBug and pytest-asyncio using pip. ```bash pip install nonebug pytest-asyncio ``` -------------------------------- ### Install NoneBug with uv Source: https://github.com/nonebot/nonebug/blob/master/README.md Install NoneBug and pytest-asyncio using uv, specifying the 'test' group. Alternatively, use anyio. ```bash uv add nonebug pytest-asyncio --group test # 或者使用 anyio uv add nonebug anyio --group test ``` -------------------------------- ### Example: Testing API Calls with App Fixture Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Demonstrates how to use the 'app' fixture to test API calls within a test context. ```python async def test_my_bot(app: App): async with app.test_api() as ctx: # Test API calls ... ``` -------------------------------- ### Example: Testing Matcher Behavior with App Fixture Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Illustrates using the 'app' fixture to test matcher behavior within a test context. ```python async def test_matcher(app: App): async with app.test_matcher() as ctx: # Test matcher behavior ... ``` -------------------------------- ### Install Nonebug and Dependencies with uv Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Add Nonebug and pytest-asyncio to your project's test group using uv. ```bash # With uv uv add nonebug pytest-asyncio --group test ``` -------------------------------- ### Install NoneBug with PDM Source: https://github.com/nonebot/nonebug/blob/master/README.md Install NoneBug and pytest-asyncio using PDM, specifying the 'test' group. Alternatively, use anyio. ```bash pdm add nonebug pytest-asyncio -dG test # 或者使用 anyio pdm add nonebug anyio -dG test ``` -------------------------------- ### Matcher Testing Workflow Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Illustrates the workflow for testing matchers with NoneBug. This includes creating a context, simulating event reception, defining expectations, and automatic verification. ```python async with app.test_matcher(matchers) as ctx: # Create instances bot = ctx.create_bot(); event = make_fake_event()() # Send event ctx.receive_event(bot, event) # Define expectations ctx.should_pass_rule(); ctx.should_paused() # Exit and verify: All expectations checked on context exit ``` -------------------------------- ### Install NoneBug with Poetry Source: https://github.com/nonebot/nonebug/blob/master/README.md Install NoneBug and pytest-asyncio using Poetry, specifying the 'test' group. Alternatively, use anyio. ```bash poetry add nonebug pytest-asyncio -G test # 或者使用 anyio poetry add nonebug anyio -G test ``` -------------------------------- ### Test GET Request Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Test an HTTP GET endpoint. This example fetches data from '/status' and verifies the status code and JSON response. ```python @pytest.mark.asyncio async def test_get_endpoint(app: App): async with app.test_server() as ctx: client = ctx.get_client() response = await client.get("/status") assert response.status_code == 200 data = response.json() assert data["status"] == "ok" ``` -------------------------------- ### Resource Management with AsyncExitStack Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Demonstrates how to use AsyncExitStack for resource cleanup within context setup. Resources are registered using `enter_async_context` or `callback` and cleaned up in reverse order upon context exit. ```python self.stack = contextlib.AsyncExitStack() # In setup(): self.stack.enter_async_context(context_manager) self.stack.callback(cleanup_function) ``` -------------------------------- ### Async Test Context Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/INDEX.md Demonstrates how to use an async context manager for NoneBug testing. Expectations are set up, code is run, and verification occurs automatically upon exiting the context. ```python async with app.test_api() as ctx: # Set up expectations # Run code # Expectations verified on exit ``` -------------------------------- ### Install NoneBug with PDM Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Add NoneBug and pytest-asyncio to your project using PDM for testing, with an option for the anyio backend. ```bash pdm add nonebug pytest-asyncio -dG test ``` ```bash # or with anyio backend pdm add nonebug anyio -dG test ``` -------------------------------- ### Install NoneBug with Poetry Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Add NoneBug and pytest-asyncio to your project using Poetry for testing, with an option for the anyio backend. ```bash poetry add nonebug pytest-asyncio -G test ``` ```bash # or with anyio backend poetry add nonebug anyio -G test ``` -------------------------------- ### Isolated Matcher Context Example (Python) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Demonstrates using the `context` manager to create an isolated matcher environment. Modifications within the `with` block do not persist after exiting. ```python # Save current state with app.provider.context(): # Matchers are isolated # Modifications don't affect the original registry pass # Original state restored # Use specific matchers test_matchers = {10: [MyMatcher]} with app.provider.context(test_matchers): # Only MyMatcher is available pass ``` -------------------------------- ### API Testing Workflow Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Demonstrates the typical workflow for API testing using NoneBug. This involves creating a context, setting up expectations, running the code, and automatic verification upon context exit. ```python async with app.test_api() as ctx: # Set up expectations ctx.should_call_api(...) # Code runs here # Expectations verified on exit ``` -------------------------------- ### Example: Mocking and Asserting API Call Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md Demonstrates how to use `should_call_api` to define an expected API call and then assert that the bot makes that call with the correct parameters and receives the expected result. ```python async with app.test_api() as ctx: adapter = ctx.create_adapter() bot = ctx.create_bot(adapter=adapter, self_id="test") ctx.should_call_api("get_user_info", {"user_id": "123"}, result={"name": "Alice"}) result = await bot.call_api("get_user_info", user_id="123") assert result == {"name": "Alice"} ``` -------------------------------- ### Setup NoneBot with Custom Initialization Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Use this fixture to provide custom initialization arguments for NoneBot, such as nickname and log level. Custom configuration values can also be set here. ```python import pytest from nonebug import NONEBOT_INIT_KWARGS @pytest.fixture(scope="session", autouse=True) def setup_nonebot(pytestconfig): pytestconfig.stash[NONEBOT_INIT_KWARGS] = { "nickname": "TestBot", "log_level": "DEBUG", "custom_key": "custom_value", # Custom config } ``` -------------------------------- ### MatcherContext Integration Example (Python) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Shows how MatcherContext uses `provider.context()` to ensure isolated matcher registries for tests, either with specified matchers or a copy of the current ones. ```python # Inside MatcherContext._prepare_matcher_context with self.app.provider.context(self.matchers) as provider: # provider now has only the specified matchers # or a copy of all matchers if self.matchers is None ... ``` -------------------------------- ### Install Nonebug and Dependencies with Poetry Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Add Nonebug and pytest-asyncio to your project's development dependencies using Poetry. ```bash # With poetry poetry add nonebug pytest-asyncio -G test ``` -------------------------------- ### Example of Using Api Data Structure Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/types.md Demonstrates how to use the Api data structure within a test context to assert an API call with specific parameters and expected results. ```python async with app.test_api() as ctx: ctx.should_call_api("get_info", {"id": "123"}, result={"name": "test"}) ``` -------------------------------- ### Context Activation Error Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Illustrates the `RuntimeError` raised when attempting to activate a second test context within an already active one. ```python async with app.test_api() as ctx1: async with app.test_api() as ctx2: # RuntimeError! ... ``` -------------------------------- ### Get Matchers with Default Value (get) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Retrieve matchers for a given priority, returning a specified default value if the priority is not found. This method provides a safe way to access registry data. ```python @overload def get(self, key: int) -> Optional[list[type[Matcher]]]: ... @overload def get(self, key: int, default: T) -> Union[list[type[Matcher]], T]: ... def get( self, key: int, default: Optional[T] = None ) -> Optional[Union[list[type[Matcher]], T]]: ... ``` -------------------------------- ### Testing with Explicit Test Client Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md This snippet shows how to test with an explicitly created `TestClient`. It's useful when you need more control over the test client setup or want to use a specific ASGI application instance. ```python from async_asgi_testclient import TestClient async def test_with_explicit_client(app: App): my_client = TestClient(nonebot.get_asgi()) # Provide the client explicitly async with app.test_server(asgi=nonebot.get_asgi()) as ctx: client = ctx.get_client() response = await client.get("/status") ``` -------------------------------- ### Example: Mocking and Asserting Message Send Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md Demonstrates how to use `should_call_send` to define an expected message send and then assert that the bot sends the specified message in response to an event. ```python async with app.test_api() as ctx: bot = ctx.create_bot(self_id="test") event = make_fake_event()() ctx.should_call_send(event, "Hello!", bot=bot) result = await bot.send(event, "Hello!") ``` -------------------------------- ### Test State Transitions with Multiple Events Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/MatcherContext.md Use this example to test state transitions of a matcher across multiple events. It verifies rule passing, message sending, and matcher states like paused and finished for each event in sequence. ```python async def test_state_transitions(app: App): from my_bot.matchers import conversation async with app.test_matcher(conversation) as ctx: bot = ctx.create_bot() msg1 = make_fake_event(_message=Message(text="start"))() msg2 = make_fake_event(_message=Message(text="continue"))() # First event - pause for next input ctx.receive_event(bot, msg1) ctx.should_pass_rule(matcher=conversation) ctx.should_call_send(msg1, "What's next?") ctx.should_paused(matcher=conversation) # Second event - continue and finish ctx.receive_event(bot, msg2) ctx.should_pass_rule(matcher=conversation) ctx.should_call_send(msg2, "Done!") ctx.should_finished(matcher=conversation) ``` -------------------------------- ### Get Items View Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Obtain a view object of (priority, matchers) pairs from the registry. This allows iterating over both priorities and their associated matcher lists simultaneously. ```python def items(self) -> ItemsView[int, list[type[Matcher]]]: ... ``` -------------------------------- ### Get or Set Default Matchers (Python) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Retrieves matchers for a given priority. If the priority is not found, it sets and returns the provided default matchers. ```python def setdefault( self, key: int, default: list[type[Matcher]] ) -> list[type[Matcher]]: ... ``` -------------------------------- ### Get Matchers by Priority (__getitem__) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Retrieve the list of matcher classes associated with a specific priority level. Raises KeyError if the priority is not found in the registry. ```python def __getitem__(self, key: int) -> list[type[Matcher]]: ... ``` -------------------------------- ### Initialization Error Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Shows the `RuntimeError` that occurs if `BaseApp.__init__` is called before NoneBot is initialized. It is recommended to use the `app` fixture provided by pytest. ```python app = App() # RuntimeError! # RuntimeError: "NoneBug is not initialized" ``` -------------------------------- ### ServerContext Async Context Manager Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md ServerContext implements the async context manager protocol, allowing it to be used with `async with`. It handles the setup and teardown of the testing environment. ```APIDOC ## ServerContext Async Context Manager ### Description `ServerContext` implements the async context manager protocol, allowing it to be used with `async with` to manage the testing environment. ### `__aenter__` Calls `setup()` to prepare the HTTP test client. ### `__aexit__` Calls `run()` to finish and cleans up resources. ### `setup()` Prepares the test environment: 1. Calls parent `setup()` (from `Context`) 2. If no test client was explicitly provided, enters the context manager for the created test client, setting up its HTTP server. ### `run()` Default implementation from parent `Context` does nothing. Subclasses can override to add post-test validation. ``` -------------------------------- ### ApiContext Async Context Manager Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md ApiContext implements the async context manager protocol for setting up and tearing down the API testing environment. __aenter__ calls setup(), and __aexit__ calls run() to validate API and send calls. ```APIDOC ## ApiContext Async Context Manager Protocol `ApiContext` implements the async context manager protocol: - **`__aenter__`** — Calls `setup()` to prepare the API testing environment, including patching all registered adapters and bots. - **`__aexit__`** — Calls `run()` to validate that all expected API/send calls were made, then cleans up resources. ### Verification on Exit When the context exits, `run()` is called, which verifies: 1. All expected API calls in the queue were made. 2. All expected send calls in the queue were made. If the queue is not empty, the test fails with a message indicating how many calls were not made. ``` -------------------------------- ### Get Priority Keys View Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Obtain a view object that displays a list of all priority levels currently in the matcher registry. This view reflects changes to the registry. ```python def keys(self) -> KeysView[int]: ... ``` -------------------------------- ### Basic Dependency Test with EventParam Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/DependentContext.md Example of testing a handler function using DependentContext, passing an event parameter, and allowing EventParam type. ```python from nonebot.params import EventParam from nonebug import App async def test_handler(app: App): def my_handler(event: Event) -> str: return f"Got event of type {event.get_type()}" FakeEvent = make_fake_event() async with app.test_dependent( my_handler, allow_types=[EventParam] ) as ctx: ctx.pass_params(event=FakeEvent()) ``` -------------------------------- ### Configure NONEBOT_START_LIFESPAN Stash Key Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Example of how to set NONEBOT_START_LIFESPAN in pytest hooks to control whether NoneBot's driver lifespan (startup/shutdown) is executed. ```python def pytest_configure(config: pytest.Config): config.stash[NONEBOT_START_LIFESPAN] = False # Skip startup/shutdown ``` -------------------------------- ### Configure NONEBOT_INIT_KWARGS Stash Key Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Passes keyword arguments to nonebot.init() via a pytest stash key. This example sets the bot's nickname and debug mode. ```python import pytest from nonebug import NONEBOT_INIT_KWARGS def pytest_configure(config: pytest.Config): config.stash[NONEBOT_INIT_KWARGS] = { "nickname": "TestBot", "debug": True, } ``` -------------------------------- ### Example of Using Send Data Structure Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/types.md Shows how to use the Send data structure to assert that a specific message is sent in response to an event, optionally specifying the bot. ```python async with app.test_api() as ctx: event = make_fake_event()() ctx.should_call_send(event, "Hello!", bot=bot) ``` -------------------------------- ### ServerContext.get_client Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md Retrieves the test client for making HTTP requests to the ASGI application. This client can be used to send GET, POST, PUT, and DELETE requests. ```APIDOC ## ServerContext.get_client ### Description Get the test client for making HTTP requests. ### Method `get_client(self) -> TestClient` ### Returns `TestClient` — The test client instance. ### Details Returns either the explicitly provided test client or the automatically created one. Use this client to make HTTP requests to the ASGI application. The returned `TestClient` (from `async_asgi_testclient`) provides methods like: - `await client.get(path, **kwargs)` — Make a GET request - `await client.post(path, **kwargs)` — Make a POST request - `await client.put(path, **kwargs)` — Make a PUT request - `await client.delete(path, **kwargs)` — Make a DELETE request ### Example ```python async with app.test_server() as ctx: client = ctx.get_client() response = await client.post("/webhook", json={"type": "message"}) assert response.status_code == 200 ``` ``` -------------------------------- ### Context Initialization and Lifecycle Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md The base `Context` class manages the test environment using an async context manager. It ensures only one context is active per app instance and handles setup, execution, and cleanup. ```python class Context: def __init__(self, app: "BaseApp", *args, **kwargs): self.app = app if self.app.context is not None: raise RuntimeError("Another test context is actived") self.app.context = self self.stack = contextlib.AsyncExitStack() async def __aenter__(self) -> Self: await self.stack.__aenter__() await self.setup() return self async def __aexit__(self, exc_type, exc_val, exc_tb): try: await self.run() finally: await self.stack.__aexit__(exc_type, exc_val, exc_tb) self.app.context = None async def setup(self) -> None: pass async def run(self) -> None: pass ``` -------------------------------- ### Test Matcher with API Calls Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Test a matcher that interacts with external APIs. This example verifies API calls, message sending, and rule passing. ```python @pytest.mark.asyncio async def test_matcher_with_api(app: App): from my_bot.matchers import user_info_matcher Message = make_fake_message() async with app.test_matcher(user_info_matcher) as ctx: adapter = ctx.create_adapter() bot = ctx.create_bot(adapter=adapter) event = make_fake_event(_message=Message(text="info"))() # Expect API call and message send ctx.should_call_api( "get_user_profile", {"user_id": "123"}, result={"name": "Alice"}, adapter=adapter ) ctx.receive_event(bot, event) ctx.should_pass_rule(matcher=user_info_matcher) ctx.should_call_send(event, "Your profile: Alice") ctx.should_finished(matcher=user_info_matcher) ``` -------------------------------- ### Get Matcher Values View Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Obtain a view object displaying all lists of matcher classes stored in the registry. Each item in the view corresponds to the matchers for a specific priority. ```python def values(self) -> ValuesView[list[type[Matcher]]]: ... ``` -------------------------------- ### Context Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Base async context manager for test contexts. It manages the lifecycle of a test context, including setup, execution, and cleanup, ensuring only one context is active per app instance. ```APIDOC ## Context ### Description Base async context manager for test contexts. It manages the lifecycle of a test context, including setup, execution, and cleanup, ensuring only one context is active per app instance. ### Properties - `app` (BaseApp): Parent application instance. - `stack` (AsyncExitStack): Stack for managing async resource cleanup. ### Async Context Manager Protocol - `__aenter__`: Enters the `AsyncExitStack`, calls `setup()`, and returns `self`. - `__aexit__`: Calls `run()`, exits the `AsyncExitStack`, and clears the app's active context. ### Subclass Hooks #### `setup()` ```python async def setup(self) -> None: pass ``` Called on `__aenter__` after entering the stack. Subclasses override to initialize resources, patch methods/attributes, or set up the test environment. #### `run()` ```python async def run(self) -> None: pass ``` Called on `__aexit__` before exiting the stack. Subclasses override to execute pending operations, validate expectations, and produce test failures if expectations are not met. Remember to call the parent `run()` if overriding in a subclass. ### Singleton Enforcement Only one `Context` can be active per `App` instance. Attempting to create a second context while one is active raises `RuntimeError`. ``` -------------------------------- ### NoneBugProvider get Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Retrieves matchers for a given priority level, returning a default value if the priority is not found. ```APIDOC ## get ### Description Get matchers with a default value. ### Parameters #### Parameters - **key** (int) - Required - Priority level. - **default** (Optional[T]) - Optional - `None` - Value to return if priority not found. ### Returns `list[type[Matcher]]` or `default` — Matchers at priority or default. ``` -------------------------------- ### Test with Custom Adapter and Bot Classes Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Demonstrates testing with custom adapter and bot subclasses. Verifies that custom classes are correctly instantiated and used by the testing framework. ```python from nonebot.adapters import Adapter, Bot class MyAdapter(Adapter): async def _call_api(self, bot, api, **data): return f"Custom: {api}" class MyBot(Bot): async def send(self, event, message, **kwargs): return f"Sent: {message}" @pytest.mark.asyncio async def test_custom_classes(app: App): async with app.test_api() as ctx: adapter = ctx.create_adapter(base=MyAdapter) bot = ctx.create_bot(base=MyBot, adapter=adapter, self_id="custom") assert isinstance(adapter, MyAdapter) assert isinstance(bot, MyBot) assert bot.self_id == "custom" ``` -------------------------------- ### BaseApp Initialization Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md The `BaseApp` constructor initializes the context and provider. It requires that NoneBot's matcher provider is already a `NoneBugProvider`, raising a `RuntimeError` if this condition is not met. ```python class BaseApp: def __init__(self): from nonebot.matcher import matchers from .provider import NoneBugProvider self.context: Optional[Context] = None if not isinstance(matchers.provider, NoneBugProvider): # pragma: no cover raise RuntimeError("NoneBug is not initialized") self.provider = matchers.provider ``` -------------------------------- ### Create Fake Adapter with ApiContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md Demonstrates creating a fake adapter using ApiContext, including using a custom base class. ```python async with app.test_api() as ctx: adapter = ctx.create_adapter() assert adapter.get_name() == "fake" # With custom base class class MyAdapter(Adapter): pass my_adapter = ctx.create_adapter(base=MyAdapter) assert isinstance(my_adapter, MyAdapter) ``` -------------------------------- ### Iterate Over Priorities (__iter__) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Get an iterator for all priority levels present in the matcher registry. This allows iterating through the keys of the registry. ```python def __iter__(self) -> Iterator[int]: ... ``` -------------------------------- ### Import Quick Reference for NoneBug Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/INDEX.md Provides a consolidated list of essential imports for using NoneBug, including the main App class, context managers, and data models. ```python # Main from nonebug import App, NONEBOT_INIT_KWARGS, NONEBOT_START_LIFESPAN # Contexts from nonebug.mixin.call_api import ApiContext from nonebug.mixin.dependent import DependentContext from nonebug.mixin.process import MatcherContext from nonebug.mixin.driver import ServerContext # Models from nonebug.mixin.call_api.model import Api, Send from nonebug.mixin.process.model import ( ReceiveEvent, RulePass, RuleNotPass, IgnoreRule, PermissionPass, PermissionNotPass, IgnorePermission, Paused, Rejected, Finished, Error ) # Provider from nonebug.provider import NoneBugProvider ``` -------------------------------- ### Initialize NoneBugProvider Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Initialize NoneBugProvider with an initial matcher registry. The registry maps integer priorities to lists of matcher classes. ```python def __init__(self, matchers: Mapping[int, list[type[Matcher]]]): ... ``` -------------------------------- ### Import ServerContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md Import the ServerContext class from the nonebug.mixin.driver module. ```python from nonebug.mixin.driver import ServerContext ``` -------------------------------- ### Testing with Custom ASGI Application Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md Use this snippet to test a custom ASGI application by passing it to `app.test_server`. It demonstrates setting up a simple Starlette app and asserting a JSON response. ```python from starlette.applications import Starlette from starlette.responses import JSONResponse async def test_custom_app(app: App): async def hello(request): return JSONResponse({"hello": "world"}) custom_asgi = Starlette(routes=[ Route("/hello", hello, methods=["GET"]) ]) async with app.test_server(asgi=custom_asgi) as ctx: client = ctx.get_client() response = await client.get("/hello") assert response.json() == {"hello": "world"} ``` -------------------------------- ### Custom ASGI App Testing Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Test server endpoints using a custom ASGI application. Provides flexibility for integrating with existing or custom server setups. ```python async with app.test_server(asgi=my_custom_asgi_app) as ctx: client = ctx.get_client() response = await client.get("/") ``` -------------------------------- ### NoneBugProvider Constructor Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Initializes the NoneBugProvider with an initial matcher registry. The registry maps priority levels to lists of matcher classes. ```APIDOC ## __init__ ### Description Initializes the NoneBugProvider with an initial matcher registry. ### Parameters #### Parameters - **matchers** (Mapping[int, list[type[Matcher]]]) - Required - Initial matcher registry. Maps priority (int) to lists of matcher classes. ``` -------------------------------- ### Get Number of Priorities (__len__) Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Retrieve the total count of unique priority levels that have registered matchers. This indicates how many different priority groups are active. ```python def __len__(self) -> int: ... ``` -------------------------------- ### Mock Adapters and Bots Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Create fake instances of adapters and bots for testing purposes. Supports generic, custom base classes, and specific bot IDs. ```python adapter = ctx.create_adapter() # Generic fake adapter bot = ctx.create_bot(self_id="my_bot", adapter=adapter) # Fake bot custom_adapter = ctx.create_adapter(base=MyAdapter) # Custom base class custom_bot = ctx.create_bot(base=MyBot) # Custom base class ``` -------------------------------- ### Test Server Workflow Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Steps to test a server endpoint using Nonebug's test client. ```python async with app.test_server() as ctx: client = ctx.get_client() # GET request response = await client.get("/status") assert response.status_code == 200 # POST request response = await client.post("/webhook", json={"data": "..."}) assert response.status_code == 200 ``` -------------------------------- ### Basic Webhook Test Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Test HTTP endpoint handling for a webhook. This example sends a POST request with JSON data and asserts a 200 status code. ```python @pytest.mark.asyncio async def test_webhook_endpoint(app: App): async with app.test_server() as ctx: client = ctx.get_client() response = await client.post( "/webhook", json={"message": "test"} ) assert response.status_code == 200 ``` -------------------------------- ### Import MatcherContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/MatcherContext.md Import the MatcherContext class from the nonebug.mixin.process module. ```python from nonebug.mixin.process import MatcherContext ``` -------------------------------- ### Verification Error Example Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Demonstrates how `run()` calls `pytest.fail()` when API call expectations are not met. This typically happens when an expected API call is not made during the test. ```python async with app.test_api() as ctx: ctx.should_call_api("test", {}) # API not called # pytest.fail("Application has 1 api call(s) not called") ``` -------------------------------- ### DriverMixin Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Adds `test_server` method to BaseApp for testing ASGI HTTP endpoints. ```APIDOC ## DriverMixin ### Description Adds the `test_server` method to `BaseApp` for testing ASGI HTTP endpoints. ### Method Added - `test_server(asgi)`: Returns a `ServerContext`. ``` -------------------------------- ### Import NoneBugProvider Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Import the NoneBugProvider class from the nonebug.provider module. ```python from nonebug.provider import NoneBugProvider ``` -------------------------------- ### Pytest Initialization Options Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Configures pytest behavior, including asyncio mode and additional command-line options for coverage and plugin management. ```toml [tool.pytest.ini_options] asyncio_mode = "auto" addopts = "--cov nonebug --cov-report term-missing -p no:nonebug" ``` -------------------------------- ### Import NoneBug Fixture Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Import the necessary fixture from the nonebug library. ```python from nonebug.fixture ``` -------------------------------- ### Utilize Testing Helper Functions Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Demonstrates the usage of utility functions like `make_fake_event` and `make_fake_message` for creating test data. ```python from tests.utils import make_fake_event, make_fake_message # Create fake event class FakeEvent = make_fake_event() event = FakeEvent() # Create fake message Message = make_fake_message() msg = Message(text="hello") # Event with message FakeEvent = make_fake_event(_message=Message()) event = FakeEvent() ``` -------------------------------- ### test_server Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/App.md Creates an ASGI server testing context to test HTTP endpoints defined in your NoneBot adapters. ```APIDOC ## test_server ### Description Create an ASGI server testing context. ### Method Signature ```python def test_server(self, asgi: Optional[ASGIApplication] = None) -> ServerContext: ... ``` ### Parameters #### `asgi` - **Type**: `Optional[ASGIApplication]` - **Default**: `None` - **Description**: ASGI application to test. If `None`, uses NoneBot's default ASGI app. ### Returns - **Type**: `ServerContext` - **Description**: An async context manager for testing ASGI server behavior. ### Usage Use this method to test HTTP endpoints defined in your NoneBot adapters. Within the context, use `get_client()` to get an HTTP test client for making requests. ### Example ```python async def test_http_endpoint(app: App): async with app.test_server() as ctx: client = ctx.get_client() response = await client.post("/webhook", json={"event": "test"}) assert response.status_code == 200 ``` ``` -------------------------------- ### Test Simple Webhook with ServerContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md A basic example of testing a webhook endpoint using ServerContext. It sends a POST request with a JSON payload and checks if the response status code is 200. ```python async def test_simple_webhook(app: App): async with app.test_server() as ctx: client = ctx.get_client() response = await client.post( "/webhook", json={"message": "test"} ) assert response.status_code == 200 ``` -------------------------------- ### BaseApp Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/BaseClasses.md Foundation application class for NoneBug. It initializes with a required NoneBugProvider and manages the active test context. ```APIDOC ## BaseApp ### Description Foundation application class for NoneBug. It initializes with a required NoneBugProvider and manages the active test context. ### Properties - `context` (Optional[Context]): Currently active context. `None` if no context is active. - `provider` (NoneBugProvider): Matcher provider for managing matchers. Must be a `NoneBugProvider`. ### Initialization Requirements `BaseApp.__init__` requires that NoneBot's matcher provider is already a `NoneBugProvider`. This is typically set up by the `_nonebot_init` pytest fixture. If called before fixture initialization, it raises `RuntimeError: "NoneBug is not initialized"`. ``` -------------------------------- ### Import App Class Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/App.md Import the App class from the nonebug library. ```python from nonebug import App ``` -------------------------------- ### Package Metadata Configuration Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Defines project name, version, description, and Python version requirements. ```toml [project] name = "nonebug" version = "0.4.4" description = "nonebot2 test framework" requires-python = ">=3.9, <4.0" ``` -------------------------------- ### Configure NoneBot with Custom Settings Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Demonstrates how to provide custom configuration to NoneBot during testing using pytest hooks. ```python # conftest.py from nonebug import NONEBOT_INIT_KWARGS def pytest_configure(config): config.stash[NONEBOT_INIT_KWARGS] = { "nickname": "TestBot", "command_start": ["/", "."], } # test_bot.py import pytest import nonebot from nonebug import App @pytest.mark.asyncio async def test_with_custom_config(app: App): config = nonebot.get_driver().config assert config.nickname == "TestBot" assert "/" in config.command_start ``` -------------------------------- ### Get Test Client from ServerContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ServerContext.md Retrieve the TestClient instance from the ServerContext. This client is used to make HTTP requests to the ASGI application. It can be either an explicitly provided client or an automatically created one. ```python async with app.test_server() as ctx: client = ctx.get_client() response = await client.post("/webhook", json={"type": "message"}) assert response.status_code == 200 ``` -------------------------------- ### Configure NONEBOT_START_LIFESPAN Stash Key Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/configuration.md Controls whether NoneBot driver startup and shutdown hooks are executed. Setting this to False skips these hooks. ```python import pytest from nonebug import NONEBOT_START_LIFESPAN def pytest_configure(config: pytest.Config): # Set to False to skip startup/shutdown config.stash[NONEBOT_START_LIFESPAN] = False ``` -------------------------------- ### Test a Simple Matcher Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Use `app.test_matcher` to test a specific matcher. This context manager allows you to create a bot, receive events, and assert matcher behavior like passing rules, permissions, sending messages, and finishing. ```python async def test_hello_matcher(app: App): from my_bot.matchers import hello async with app.test_matcher(hello) as ctx: bot = ctx.create_bot() event = make_fake_event(_message=Message(text="hello"))() ctx.receive_event(bot, event) ctx.should_pass_rule(matcher=hello) ctx.should_pass_permission(matcher=hello) ctx.should_call_send(event, "Hello there!") ctx.should_finished(matcher=hello) ``` -------------------------------- ### Test ASGI Server Context Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/App.md Use `test_server` to create an ASGI server testing context for testing HTTP endpoints. Obtain an HTTP test client using `get_client()` to make requests. ```python def test_server(self, asgi: Optional[ASGIApplication] = None) -> ServerContext: ... ``` ```python async def test_http_endpoint(app: App): async with app.test_server() as ctx: client = ctx.get_client() response = await client.post("/webhook", json={"event": "test"}) assert response.status_code == 200 ``` -------------------------------- ### NoneBugProvider keys Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/NoneBugProvider.md Returns a view object that displays a list of all the priority levels in the matcher registry. ```APIDOC ## keys ### Description Get a view of priority levels. ### Returns `KeysView[int]` — View of all priorities. ``` -------------------------------- ### Test Dependency Workflow Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Steps to test a dependency using Nonebug's context manager. ```python async with app.test_dependent(handler) as ctx: ctx.pass_params(event=FakeEvent(), state={}) ctx.should_return({"processed": True}) ``` -------------------------------- ### Test Specific Matchers with MatcherContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/MatcherContext.md This snippet demonstrates testing specific matchers by providing a list of matchers to the context. It asserts rule and permission outcomes for individual matchers and checks for expected messages and matcher completion. ```python async def test_specific_matcher(app: App): from my_bot.matchers import greet_matcher, admin_matcher async with app.test_matcher([greet_matcher, admin_matcher]) as ctx: bot = ctx.create_bot() event = make_fake_event()() ctx.receive_event(bot, event) ctx.should_pass_rule(matcher=greet_matcher) ctx.should_not_pass_rule(matcher=admin_matcher) ctx.should_call_send(event, "Hi!") ctx.should_finished(matcher=greet_matcher) ``` -------------------------------- ### Session-Scoped nonebug_init Fixture Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/fixtures.md Sets up the NoneBot driver's lifespan, including startup and shutdown hooks. It respects the NONEBOT_START_LIFESPAN stash key to control whether the lifespan context is run. ```python @pytest.fixture(scope="session", autouse=True) async def nonebug_init( _nonebot_init: None, after_nonebot_init: None, request: pytest.FixtureRequest ): run_lifespan = request.config.stash.get(NONEBOT_START_LIFESPAN, True) ctx = lifespan_ctx() if run_lifespan else nullcontext() async with ctx: yield ``` -------------------------------- ### Parameter Injection for Dependencies Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Pass specific parameters like events and states to dependencies during testing. Allows for fine-grained control over dependency inputs. ```python from nonebot.params import EventParam, StateParam async with app.test_dependent(handler, allow_types=[EventParam, StateParam]) as ctx: ctx.pass_params(event=FakeEvent(), state={}) ``` -------------------------------- ### Matcher Action Checks Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Define expected state transitions for matchers. Each matcher can only have one action per event. ```python ctx.should_paused(matcher=my_matcher) # Matcher pauses (PausedException) ctx.should_rejected(matcher=my_matcher) # Matcher rejects (RejectedException) ctx.should_finished(matcher=my_matcher) # Matcher finishes (FinishedException) ``` -------------------------------- ### NoneBug Project Navigation Structure Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/MANIFEST.md This structure outlines the main documentation files and their hierarchical relationships within the NoneBug project. It helps users navigate through different aspects of the project's documentation, from the main index to API references. ```markdown INDEX.md (main hub) ├── README.md (quick start) ├── examples.md (practical examples) ├── types.md (data structures) ├── fixtures.md (pytest fixtures) ├── configuration.md (setup) └── api-reference/ ├── App.md ├── ApiContext.md ├── MatcherContext.md ├── DependentContext.md ├── ServerContext.md ├── NoneBugProvider.md └── BaseClasses.md ``` -------------------------------- ### create_adapter Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md Creates a fake adapter for testing purposes. This adapter's API call method is patched to integrate with the ApiContext's call verification system. ```APIDOC ## create_adapter ### Description Creates a fake adapter for testing. This adapter's API call method is patched to integrate with the ApiContext's call verification system. ### Method `create_adapter(self, *, base: Optional[type[A]] = None, **kwargs) -> Union[A, Adapter]` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters * `base` (Optional[type[A]]) - Optional - Base adapter class to extend. If `None`, uses `nonebot.adapters.Adapter`. The fake adapter inherits from this class. * `**kwargs` (Any) - Optional - Additional keyword arguments passed to the adapter constructor. ### Returns `Adapter` or `A` — A fake adapter instance with `_call_api` patched to use the context's `got_call_api()` method. ### Example ```python async with app.test_api() as ctx: adapter = ctx.create_adapter() assert adapter.get_name() == "fake" # With custom base class class MyAdapter(Adapter): pass my_adapter = ctx.create_adapter(base=MyAdapter) assert isinstance(my_adapter, MyAdapter) ``` ``` -------------------------------- ### Rule and Permission Checks Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/README.md Verify how matchers handle rule and permission checks. Use these to assert expected outcomes or force specific behaviors. ```python ctx.should_pass_rule(matcher=my_matcher) # Expects rule check to return True ctx.should_not_pass_rule(matcher=my_matcher) # Expects rule check to return False ctx.should_ignore_rule(matcher=my_matcher) # Force rule to pass ctx.should_pass_permission(matcher=my_matcher) # Expects permission check to return True ctx.should_not_pass_permission(matcher=my_matcher) # Expects permission check to return False ctx.should_ignore_permission(matcher=my_matcher) # Force permission to pass ``` -------------------------------- ### Create Fake Bot with ApiContext Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/ApiContext.md Shows how to create a fake bot instance within an ApiContext, specifying self_id and associating it with an adapter. It also verifies that the bot is registered with NoneBot. ```python async with app.test_api() as ctx: adapter = ctx.create_adapter() bot = ctx.create_bot(self_id="my_bot", adapter=adapter) # bot is now registered with NoneBot from nonebot import get_bot assert get_bot("my_bot") is bot ``` -------------------------------- ### test_api Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/api-reference/App.md Creates an API testing context for verifying bot API calls and message sending. Within this context, you can set up fake adapters and bots, define expected API calls using `should_call_api()`, and assert their occurrence with `got_call_api()`. ```APIDOC ## test_api ### Description Create an API testing context. ### Method `test_api(self) -> ApiContext` ### Returns `ApiContext` — An async context manager for testing bot API calls and message sending. ### Detailed Description Use this method to test how the bot's adapters and bots call APIs or send messages. Within this context, you can create fake adapters and bots, define expected API calls using `should_call_api()`, and verify they occur with `got_call_api()`. ### Example ```python async def test_api_calling(app: App): async with app.test_api() as ctx: adapter = ctx.create_adapter() bot = ctx.create_bot(self_id="test_bot", adapter=adapter) ctx.should_call_api("get_user_info", {"user_id": "123"}, result={"name": "Alice"}) result = await bot.call_api("get_user_info", user_id="123") assert result == {"name": "Alice"} ``` ``` -------------------------------- ### Test Multiple Sequential API Calls Source: https://github.com/nonebot/nonebug/blob/master/_autodocs/examples.md Verify the correct execution of multiple sequential API calls. Ensures 'get_user_info' is called first, followed by 'get_user_rank' using the result from the first call. ```python @pytest.mark.asyncio async def test_multiple_apis(app: App): async with app.test_api() as ctx: bot = ctx.create_bot(self_id="my_bot") # Define first call ctx.should_call_api( "get_user_info", {"user_id": "123"}, result={"id": "123", "level": 5} ) # Define second call ctx.should_call_api( "get_user_rank", {"level": 5}, result={"rank": "Gold"} ) # Make calls in order user = await bot.call_api("get_user_info", user_id="123") rank = await bot.call_api("get_user_rank", level=user["level"]) assert rank["rank"] == "Gold" ```