# Test Generation Steps This is a reference for instances of `Step` used in test generation. They are used when implementing a [step hook](../howto/customize-testgen) or a [plan hook](../howto/customize-testgen). All steps can be imported from `kolo.generate_tests.steps`. ## `AssertDelete` The `AssertDelete` step represents an assertion that no instances of a Django model exist. ### `AssertDelete.module` The module where the Django model lives. For example, `"django.contrib.auth.models"`. ### `AssertDelete.model` The name of the Django model. For example, `"User"`. ### `AssertDelete.fields` A list of [`DjangoField`](#djangofield) instances used to specify which model instances do not exist. ## `AssertEqual` The `AssertEqual` step represents an assertion that two values are equal. ### `AssertEqual.left` The `repr` of a Python object to compare. For example `"user.username"`. ### `AssertEqual.right` The `repr` of a Python object to compare. For example `'"admin"'`. ### `AssertEqual.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. Defaults to an empty set. ## `AssertInsert` The `AssertInsert` step represents an assertion that a Django model instance has been created. ### `AssertInsert.module` The module where the Django model lives. For example, `"django.contrib.auth.models"`. ### `AssertInsert.model` The name of the Django model. For example, `"User"`. ### `AssertInsert.lookup_fields` A list of [`DjangoField`](#djangofield) instances used to retrieve the model instance from the database. ### `AssertInsert.assert_fields` A list of [`DjangoField`](#djangofield) instances used to assert the inserted model has the correct values. ### `AssertInsert.defines_variable_name` A string representing the variable name created by this step. For example `"user"`. ## `AssertResponseJson` The `AssertResponseJson` step represents an assertion that a `response` created by a [`DjangoTestClient`](#djangotestclient) step has a given json body. ### `AssertResponseJson.response_json` The expected body of the response, decoded from json. ## `AssertStatusCode` The `AssertStatusCode` step represents an assertion that a `response` created by a [`DjangoTestClient`](#djangotestclient) step has a given status code. ### `AssertStatusCode.status_code` The expected status code integer. For example, `200`. ## `AssertTemplateUsed` The `AssertTemplateUsed` step represents an assertion that a `response` created by a [`DjangoTestClient`](#djangotestclient) step was generated using a particular template. ### `AssertTemplateUsed.template_name` The name of the template. For example, `"users/profile.html"`. ## `AssertUpdate` The `AssertUpdate` step represents an assertion that a Django model instance has been updated. ### `AssertUpdate.model` The name of the Django model. For example, `"User"`. ### `AssertUpdate.fields` A list of [`DjangoField`](#djangofield) instances used to assert the updated model has the correct values. ### `AssertUpdate.references_variable_name` A string representing the variable name used by this step. For example `"user"`. This is expected to be created by an earlier step. ## `Code` The `Code` step represents an arbitrary chunk of code. It can be used when no other step is suitable. ### `Code.code` A string representing the code. ### `Code.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. Defaults to an empty set. ## `CodeComment` The `CodeComment` step represents a comment in the source code of the generated test. ### `CodeComment.comment` The text of the comment. For example, `"Generated by Kolo"`. ## `DjangoTestClient` The `DjangoTestClient` step represents a call to the Django test client. ### `DjangoTestClient.method` The name of the http method used by the request. For example, `get` or `post`. ### `DjangoTestClient.path_info` The url fragment for the view being called by the test client. For example, `"/users/"`. ### `DjangoTestClient.query_params` The request params used by a `GET` call. For example, `{"name": "Kolo"}`. ### `DjangoTestClient.request_body` The request body used by a `POST`, `PUT` or `PATCH` call. For example, `{"name": "Kolo"}`. ### `DjangoTestClient.headers`. A dictionary containing any request headers to include in the test client call. For example, `{"user-agent": "curl/7.79.1"}`. ## `EmptyLine` The `EmptyLine` step represents an empty line of source code in the generated test. ## `FactoryCreate` The `FactoryCreate` step represents the creation of a Django model instance with a [factory boy](https://factoryboy.readthedocs.io/en/stable/) factory. ### `FactoryCreate.module` The module where the factory lives. For example, `"users.tests.factories"`. ### `FactoryCreate.factory` The name of the factory. For example, `"UserFactory"`. ### `FactoryCreate.fields` A list of [`DjangoField`](#djangofield) instances defining the created model instance. ### `FactoryCreate.defines_variable_name` A string representing the variable name created by this step. For example `"user"`. ## `ModelCreate` The `ModelCreate` step represents the creation of a Django model instance. ### `ModelCreate.module` The module where the model lives. For example, `"django.contrib.auth.models"`. ### `ModelCreate.model` The name of the model. For example, `"User"`. ### `ModelCreate.fields` A list of [`DjangoField`](#djangofield) instances defining the created model instance. ### `ModelCreate.defaults` A list of [`DjangoField`](#djangofield) instances passed as the `defaults` argument to `get_or_create`. ### `ModelCreate.defines_variable_name` A string representing the variable name created by this step. For example `"user"`. ### `ModelCreate.method` The name of the Django manager method used to create the model. Defaults to `get_or_create`. ## `ModelUpdate` The `ModelUpdate` step represents an update to an already created Django model. ### `ModelUpdate.model` The name of the Django model. For example, `"User"`. ### `ModelUpdate.fields` A list of [`DjangoField`](#djangofield) instances used to update the model. ### `ModelUpdate.references_variable_name` A string representing the variable name used by this step. For example `"user"`. This is expected to be created by a [`ModelCreate`](#modelcreate) step. ## `ModuleImports` The `ModuleImports` step represents a location in the generated test where imports defined by other steps should be placed. ## `RegisterMocket` The `RegisterMocket` step represents the configuration of [`mocket`](https://pypi.org/project/mocket/) to mock http calls. ### `RegisterMocket.method` The http method of the mocked endpoint. For example, `GET`. ### `RegisterMocket.url` The url of the mocked endpoint. For example, `"https://example.com"`. ### `RegisterMocket.status_code` The status code for mocket to return. For example, `200`. ### `RegisterMocket.body` The body of the mocked response. For example, `"
Hello world!
"`. Defaults to `None`. ### `RegisterMocket.json_body` The body of the mocked response for a json endpoint. This should be a python object that can be serialized as json. For example, `{"foo": False}`. Defaults to `None`. ### `RegisterMocket.content_type` The content type of the mocked response. For example, `"text/plain"`. Defaults to `""`. ## `StartTimeTravel` The `StartTimeTravel` represents a context manager configuring [`time-machine`](https://pypi.org/project/time-machine/) or [`freezegun`](https://pypi.org/project/freezegun/) to mock time during the generated test. It must always be paired with an `EndTimeTravel` step to mark the end of the indented block. ### `StartTimeTravel.call` The time travel function to call. Either `time_machine.travel` or `freezegun.freeze_time`. ### `StartTimeTravel.args` A tuple containing the function arguments to the time travel function. For example, `('"2024-05-29T12:54:42"',)` or `('"2024-05-29T12:54:42"', "tick=True")`. ### `StartTimeTravel.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. Either `{Import("import time_machine")}` or `{Import("import freezegun")}`. ## `TestClass` The `TestClass` step represents a test class in the [`unittest`](https://docs.python.org/3/library/unittest.html) style. It must always be paired with an `EndTestClass` step to mark the end of the indented block. ### `TestClass.name` The name of the generated test class. For example, `"MyTestCase"`. ### `TestClass.parents` A tuple containing the parent classes of the generated test class. For example, `("TestCase",)`. ### `TestClass.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. For example, `{Import("from django.test import TestCase")}`. ## `TestFunction` The `TestFunction` step represents a test function in the [`pytest`](https://pytest.org) style. It must always be paired with an `EndTestFunction` step to mark the end of the indented block. ### `TestFunction.name` The name of the test function. For example, `"test_my_view"`. ### `TestFunction.fixtures` A tuple containing any [pytest fixtures](https://docs.pytest.org/en/latest/explanation/fixtures.html) for use in the test. For example, `("client",)`. ## `TestMethod` The `TestMethod` step represents a test method in the [`unittest`](https://docs.python.org/3/library/unittest.html) style. It must always be paired with an `EndTestMethod` step to mark the end of the indented block. ### `TestMethod.name` The name of the test method. Defaults to `"test_my_view"`. ## `With` The `With` step represents a context manager. It must always be paired with an `EndWith` step to mark the end of the indented block. ### `With.call` The name of the context manager. For example, `"pytest.raises"`. ### `With.args` A tuple containing any arguments passed to the context manager. For example, `("ValueError", 'match="must be 0 or None"')`. ### `With.defines_variable_name` A string representing the variable name created by this step. For example `"exc_info"`. ### `With.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. For example, `{Import("import pytest")}`. ## `DjangoField` The `DjangoField` class is a helper class representing a Django field that is used by another step. ### `DjangoField.name` The name of the field. For example, `"username"`. ### `DjangoField.value` The `repr` of the value of the field. For example, `'"admin"'`. ### `DjangoField.imports` A `set` of [`Import`](#import) instances used to add required imports to the generated test. For example, `{Import("from decimal import Decimal")}`. ## `Import` The `Import` class is a helper class representing an import that will be included in the generated test by the `ModuleImports` step. ### `Import.import_path` The import to be include in the test. For example, `"from datetime import date"`. ## `Step` The `Step` class can be subclassed to define a custom step. ### `Step.indent_delta` This class attribute marks a change in the indentation level of the generated code. Steps that start an indented block should set this to `1`. Steps that end an indented block should set this to `-1`. All other steps can inherit the default value of `0`. ### `Step.render` A `Step` subclass must define a `render` method that defines how that step should be rendered in the generated test. This method takes a boolean `pytest` argument to allow the rendered step to differ between pytest and unittest style tests. It must return a string. For example: ``` from kolo.generate_tests.steps import Step @dataclass() class AssertGreater(Step): left: str right: str def render(self, pytest): if pytest: return f"assert {self.left} > {self.right}\n" return f"self.assertGreater({self.left}, {self.right})\n" ``` ### `Step.get_imports` A `Step` subclass may define a `get_imports` method that returns a `set` of [`Import`](#import) instances for inclusion in the generated test. This method takes a boolean `pytest` argument to allow different imports for pytest and unittest style tests. For example: ``` from kolo.generate_tests.steps import Step @dataclass() class AssertTemplateNotUsed(Step): template_name: str def render(self, pytest): if pytest: assertTemplateNotUsed = "assertTemplateNotUsed" else: assertTemplateNotUsed = "self.assertTemplateNotUsed" return f"{assertTemplateNotUsed}(response, {repr(self.template_name)})\n" def get_imports(self, pytest): if pytest: return {Import("from pytest_django.asserts import assertTemplateNotUsed")} return set() ``` ### `Step.get_steps` A `Step` subclass that represents the start of a section should define a `get_steps` classmethod that returns the start step, the end step and all steps in between. This is used to determine which steps are passed to a `step_hook` targetting this start step. The `get_steps_slice` helper function is available to make implementing this easy: ``` from kolo.generate_tests.steps import Step, get_steps_slice @dataclass() class Start(Step): indent_delta = 1 @classmethod def get_steps(cls, steps): return get_steps_slice(cls, End, steps) @dataclass() class End(Step): indent_delta = -1 ```