!!! info "" This documentation applies for both creating tests in ANTA or creating your own test package. ANTA is not only a Python library with a CLI and a collection of built-in tests, it is also a framework you can extend by building your own tests. ## Generic approach A test is a Python class where a test function is defined and will be run by the framework. ANTA provides an abstract class [AntaTest](../api/models.md#anta.models.AntaTest). This class does the heavy lifting and provide the logic to define, collect and test data. The code below is an example of a simple test in ANTA, which is an [AntaTest](../api/models.md#anta.models.AntaTest) subclass: ```python from anta.models import AntaTest, AntaCommand from anta.decorators import skip_on_platforms class VerifyTemperature(AntaTest): """Verifies if the device temperature is within acceptable limits. Expected Results ---------------- * Success: The test will pass if the device temperature is currently OK: 'temperatureOk'. * Failure: The test will fail if the device temperature is NOT OK. Examples -------- ```yaml anta.tests.hardware: - VerifyTemperature: ``` """ name = "VerifyTemperature" description = "Verifies the device temperature." categories: ClassVar[list[str]] = ["hardware"] commands: ClassVar[list[AntaCommand | AntaTemplate]] = [AntaCommand(command="show system environment temperature", revision=1)] @skip_on_platforms(["cEOSLab", "vEOS-lab", "cEOSCloudLab"]) @AntaTest.anta_test def test(self) -> None: """Main test function for VerifyTemperature.""" command_output = self.instance_commands[0].json_output temperature_status = command_output.get("systemStatus", "") if temperature_status == "temperatureOk": self.result.is_success() else: self.result.is_failure(f"Device temperature exceeds acceptable limits. Current system status: '{temperature_status}'") ``` [AntaTest](../api/models.md#anta.models.AntaTest) also provide more advanced capabilities like [AntaCommand](../api/models.md#anta.models.AntaCommand) templating using the [AntaTemplate](../api/models.md#anta.models.AntaTemplate) class or test inputs definition and validation using [AntaTest.Input](../api/models.md#anta.models.AntaTest.Input) [pydantic](https://docs.pydantic.dev/latest/) model. This will be discussed in the sections below. ## AntaTest structure Full AntaTest API documentation is available in the [API documentation section](../api/models.md#anta.models.AntaTest) ### Class Attributes - `name` (`str`): Name of the test. Used during reporting. - `description` (`str`): A human readable description of your test. - `categories` (`list[str]`): A list of categories in which the test belongs. - `commands` (`[list[AntaCommand | AntaTemplate]]`): A list of command to collect from devices. This list __must__ be a list of [AntaCommand](../api/models.md#anta.models.AntaCommand) or [AntaTemplate](../api/models.md#anta.models.AntaTemplate) instances. Rendering [AntaTemplate](../api/models.md#anta.models.AntaTemplate) instances will be discussed later. !!! info All these class attributes are mandatory. If any attribute is missing, a `NotImplementedError` exception will be raised during class instantiation. ### Instance Attributes !!! info You can access an instance attribute in your code using the `self` reference. E.g. you can access the test input values using `self.inputs`. ::: anta.models.AntaTest options: show_docstring_attributes: true show_root_heading: false show_bases: false show_docstring_description: false show_docstring_examples: false show_docstring_parameters: false members: false show_source: false show_root_toc_entry: false heading_level: 10 !!! note "Logger object" ANTA already provides comprehensive logging at every steps of a test execution. The [AntaTest](../api/models.md#anta.models.AntaTest) class also provides a `logger` attribute that is a Python logger specific to the test instance. See [Python documentation](https://docs.python.org/3/library/logging.html) for more information. !!! note "AntaDevice object" Even if `device` is not a private attribute, you should not need to access this object in your code. ### Test Inputs [AntaTest.Input](../api/models.md#anta.models.AntaTest.Input) is a [pydantic model](https://docs.pydantic.dev/latest/usage/models/) that allow test developers to define their test inputs. [pydantic](https://docs.pydantic.dev/latest/) provides out of the box [error handling](https://docs.pydantic.dev/latest/usage/models/#error-handling) for test input validation based on the type hints defined by the test developer. The base definition of [AntaTest.Input](../api/models.md#anta.models.AntaTest.Input) provides common test inputs for all [AntaTest](../api/models.md#anta.models.AntaTest) instances: #### Input model Full `Input` model documentation is available in [API documentation section](../api/models.md#anta.models.AntaTest.Input) ::: anta.models.AntaTest.Input options: show_docstring_attributes: true show_root_heading: false show_category_heading: false show_bases: false show_docstring_description: false show_docstring_examples: false show_docstring_parameters: false show_source: false members: false show_root_toc_entry: false heading_level: 10 #### ResultOverwrite model Full `ResultOverwrite` model documentation is available in [API documentation section](../api/models.md#anta.models.AntaTest.Input.ResultOverwrite) ::: anta.models.AntaTest.Input.ResultOverwrite options: show_docstring_attributes: true show_root_heading: false show_category_heading: false show_bases: false show_docstring_description: false show_docstring_examples: false show_docstring_parameters: false show_source: false show_root_toc_entry: false heading_level: 10 !!! note The pydantic model is configured using the [`extra=forbid`](https://docs.pydantic.dev/latest/usage/model_config/#extra-attributes) that will fail input validation if extra fields are provided. ### Methods - [test(self) -> None](../api/models.md#anta.models.AntaTest.test): This is an abstract method that __must__ be implemented. It contains the test logic that can access the collected command outputs using the `instance_commands` instance attribute, access the test inputs using the `inputs` instance attribute and __must__ set the `result` instance attribute accordingly. It must be implemented using the `AntaTest.anta_test` decorator that provides logging and will collect commands before executing the `test()` method. - [render(self, template: AntaTemplate) -> list[AntaCommand]](../api/models.md#anta.models.AntaTest.render): This method only needs to be implemented if [AntaTemplate](../api/models.md#anta.models.AntaTemplate) instances are present in the `commands` class attribute. It will be called for every [AntaTemplate](../api/models.md#anta.models.AntaTemplate) occurrence and __must__ return a list of [AntaCommand](../api/models.md#anta.models.AntaCommand) using the [AntaTemplate.render()](../api/models.md#anta.models.AntaTemplate.render) method. It can access test inputs using the `inputs` instance attribute. ## Test execution Below is a high level description of the test execution flow in ANTA: 1. ANTA will parse the test catalog to get the list of [AntaTest](../api/models.md#anta.models.AntaTest) subclasses to instantiate and their associated input values. We consider a single [AntaTest](../api/models.md#anta.models.AntaTest) subclass in the following steps. 2. ANTA will instantiate the [AntaTest](../api/models.md#anta.models.AntaTest) subclass and a single device will be provided to the test instance. The `Input` model defined in the class will also be instantiated at this moment. If any [ValidationError](https://docs.pydantic.dev/latest/errors/errors/) is raised, the test execution will be stopped. 3. If there is any [AntaTemplate](../api/models.md#anta.models.AntaTemplate) instance in the `commands` class attribute, [render()](../api/models.md#anta.models.AntaTest.render) will be called for every occurrence. At this moment, the `instance_commands` attribute has been initialized. If any rendering error occurs, the test execution will be stopped. 4. The `AntaTest.anta_test` decorator will collect the commands from the device and update the `instance_commands` attribute with the outputs. If any collection error occurs, the test execution will be stopped. 5. The [test()](../api/models.md#anta.models.AntaTest.test) method is executed. ## Writing an AntaTest subclass In this section, we will go into all the details of writing an [AntaTest](../api/models.md#anta.models.AntaTest) subclass. ### Class definition Import [anta.models.AntaTest](../api/models.md#anta.models.AntaTest) and define your own class. Define the mandatory class attributes using [anta.models.AntaCommand](../api/models.md#anta.models.AntaCommand), [anta.models.AntaTemplate](../api/models.md#anta.models.AntaTemplate) or both. !!! info Caching can be disabled per `AntaCommand` or `AntaTemplate` by setting the `use_cache` argument to `False`. For more details about how caching is implemented in ANTA, please refer to [Caching in ANTA](../advanced_usages/caching.md). ```python from anta.models import AntaTest, AntaCommand, AntaTemplate class (AntaTest): """ """ name = "YourTestName" # should be your class name description = "" categories = ["", ""] commands = [ AntaCommand( command="", ofmt="", version="", revision="", # revision has precedence over version use_cache="", ), AntaTemplate( template="", ofmt="", version="", revision="", # revision has precedence over version use_cache="", ) ] ``` ### Inputs definition If the user needs to provide inputs for your test, you need to define a [pydantic model](https://docs.pydantic.dev/latest/usage/models/) that defines the schema of the test inputs: ```python class (AntaTest): """Verifies ... Expected Results ---------------- * Success: The test will pass if ... * Failure: The test will fail if ... Examples -------- ```yaml your.module.path: - YourTestName: field_name: example_field_value ``` """ ... class Input(AntaTest.Input): """Inputs for my awesome test.""" : """""" ``` To define an input field type, refer to the [pydantic documentation](https://docs.pydantic.dev/latest/usage/types/types/) about types. You can also leverage [anta.custom_types](../api/types.md) that provides reusable types defined in ANTA tests. Regarding required, optional and nullable fields, refer to this [documentation](https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields) on how to define them. !!! note All the `pydantic` features are supported. For instance you can define [validators](https://docs.pydantic.dev/latest/usage/validators/) for complex input validation. ### Template rendering Define the `render()` method if you have [AntaTemplate](../api/models.md#anta.models.AntaTemplate) instances in your `commands` class attribute: ```python class (AntaTest): ... def render(self, template: AntaTemplate) -> list[AntaCommand]: return [template.render(