|
2 | 2 |
|
3 | 3 | import re
|
4 | 4 | from typing import List, Tuple, Union, cast
|
| 5 | +from unittest import TestCase |
5 | 6 |
|
6 | 7 | import pytest
|
| 8 | +from django.contrib.auth import authenticate |
| 9 | +from django.http import HttpRequest |
| 10 | +from django.test.client import Client as _TestClient |
7 | 11 | from typing_extensions import TypedDict
|
8 | 12 | from xdist import is_xdist_worker
|
9 | 13 |
|
@@ -81,3 +85,56 @@ def validate_requirement_tagging(item: pytest.Item) -> RequirementValidationResu
|
81 | 85 | "errors": errors,
|
82 | 86 | "validated_requirements": validated_requirements,
|
83 | 87 | }
|
| 88 | + |
| 89 | + |
| 90 | +class TestClient(_TestClient): |
| 91 | + """ |
| 92 | + A Wrapper around Django's default TestClient to add a few extra features, |
| 93 | + such as compatibility with `django-axes` |
| 94 | + """ |
| 95 | + |
| 96 | + # This is so that Pytest doesn't think this is itself a test |
| 97 | + __test__ = False |
| 98 | + |
| 99 | + def login(self, client_ip="127.0.0.1", **credentials) -> bool: |
| 100 | + """ |
| 101 | + This overrides the `login` method from `ClientMixin`, to get it to work with |
| 102 | + the `django-axes` middleware, which requires a `HttpRequest` object as part of the |
| 103 | + authentication flow. |
| 104 | + """ |
| 105 | + request = HttpRequest() |
| 106 | + request.META = {"REMOTE_ADDR": client_ip} |
| 107 | + user = authenticate(request=request, **credentials) |
| 108 | + if user: |
| 109 | + self._login(user) # type: ignore[attr-defined] |
| 110 | + return True |
| 111 | + return False |
| 112 | + |
| 113 | + |
| 114 | +class TestDataManager(TestCase): |
| 115 | + ''' |
| 116 | + Wrapper class to help manage test data through the lifecycle of a test |
| 117 | +
|
| 118 | + The normal pattern for using this would be sub-class it, and then expose it |
| 119 | + as a injected fixture. E.g.: |
| 120 | +
|
| 121 | + ```python |
| 122 | + class MyTestDataManager(TestDataManager): |
| 123 | + pass |
| 124 | +
|
| 125 | + @pytest.fixture |
| 126 | + def test_data(db) -> TestDataManager: |
| 127 | + """ |
| 128 | + Fixture wrapper around test data manager |
| 129 | + """ |
| 130 | + return TestDataManager() |
| 131 | + ``` |
| 132 | + ''' |
| 133 | + |
| 134 | + # Note: This overrides the default of the parent class, to use our |
| 135 | + # modified TestClient |
| 136 | + client: TestClient |
| 137 | + |
| 138 | + def __init__(self) -> None: |
| 139 | + super().__init__() |
| 140 | + self.client = TestClient() |
0 commit comments