feat: add decorator for writing component tests (#1008)

* feat: add decorator for writing component tests

* refactor: udpate changelog + update deps pins

* refactor: fix deps

* refactor: make cached_ref into generic and fix linter errors

* refactor: fix coverage testing

* refactor: use global var instead of env var for is_testing state
This commit is contained in:
Juro Oravec 2025-03-02 19:46:12 +01:00 committed by GitHub
parent 81ac59f7fb
commit 7dfcb447c4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 4428 additions and 3661 deletions

View file

@ -8,4 +8,5 @@ nav:
- Typing and validation: typing_and_validation.md
- Custom template tags: template_tags.md
- Tag formatters: tag_formatter.md
- Testing: testing.md
- Authoring component libraries: authoring_component_libraries.md

View file

@ -0,0 +1,111 @@
_New in version 0.131_
The [`@djc_test`](../../../reference/testing_api#djc_test) decorator is a powerful tool for testing components created with `django-components`. It ensures that each test is properly isolated, preventing components registered in one test from affecting others.
## Usage
The [`@djc_test`](../../../reference/testing_api#djc_test) decorator can be applied to functions, methods, or classes. When applied to a class, it recursively decorates all methods starting with `test_`, including those in nested classes. This allows for comprehensive testing of component behavior.
### Applying to a Function
To apply [`djc_test`](../../../reference/testing_api#djc_test) to a function,
simply decorate the function as shown below:
```python
import django
from django_components.testing import djc_test
django.setup()
@djc_test
def test_my_component():
@register("my_component")
class MyComponent(Component):
template = "..."
...
```
### Applying to a Class
When applied to a class, `djc_test` decorates each `test_` method individually:
```python
import django
from django_components.testing import djc_test
django.setup()
@djc_test
class TestMyComponent:
def test_something(self):
...
class Nested:
def test_something_else(self):
...
```
This is equivalent to applying the decorator to each method individually:
```python
import django
from django_components.testing import djc_test
django.setup()
class TestMyComponent:
@djc_test
def test_something(self):
...
class Nested:
@djc_test
def test_something_else(self):
...
```
### Arguments
See the API reference for [`@djc_test`](../../../reference/testing_api#djc_test) for more details.
### Setting Up Django
Before using [`djc_test`](../../../reference/testing_api#djc_test), ensure Django is set up:
```python
import django
from django_components.testing import djc_test
django.setup()
@djc_test
def test_my_component():
...
```
## Example: Parametrizing Context Behavior
You can parametrize the [context behavior](../../../reference/settings#django_components.app_settings.ComponentsSettings.context_behavior) using [`djc_test`](../../../reference/testing_api#djc_test):
```python
from django_components.testing import djc_test
@djc_test(
# Settings applied to all cases
components_settings={
"app_dirs": ["custom_dir"],
},
# Parametrized settings
parametrize=(
["components_settings"],
[
[{"context_behavior": "django"}],
[{"context_behavior": "isolated"}],
],
["django", "isolated"],
)
)
def test_context_behavior(components_settings):
rendered = MyComponent().render()
...
```