# Client Testing

Django test client and request factory fixtures for HTTP testing, view testing, and API testing. These fixtures provide access to Django's test client for making HTTP requests and testing views.

## Capabilities

### Django Test Client

Standard Django test client for making HTTP requests in tests.

```python { .api }
def client() -> django.test.Client:
    """
    Django test client for HTTP requests and view testing.
    
    Provides a test client that can make GET, POST, PUT, DELETE, and other
    HTTP requests to your Django views. Automatically handles CSRF tokens,
    sessions, and authentication state.
    
    Returns:
        django.test.Client: Configured test client instance
    """
```

Usage example:
```python
def test_view_response(client):
    # GET request
    response = client.get("/my-view/")
    assert response.status_code == 200
    assert b"Hello World" in response.content
    
    # POST request with data
    response = client.post("/submit/", {"name": "test", "email": "test@example.com"})
    assert response.status_code == 302
    
    # JSON POST request
    import json
    response = client.post(
        "/api/data/", 
        json.dumps({"key": "value"}),
        content_type="application/json"
    )
    assert response.status_code == 201
```

### Async Django Test Client

Async version of Django test client for testing async views and ASGI applications.

```python { .api }
def async_client() -> django.test.AsyncClient:
    """
    Django async test client for async view testing.
    
    Async version of Django's test client for testing async views,
    ASGI applications, and async middleware. Supports all HTTP methods
    with async/await syntax.
    
    Returns:
        django.test.AsyncClient: Configured async test client instance
    """
```

Usage example:
```python
import pytest

@pytest.mark.asyncio
async def test_async_view(async_client):
    # Async GET request
    response = await async_client.get("/async-view/")
    assert response.status_code == 200
    
    # Async POST request
    response = await async_client.post("/async-submit/", {"data": "test"})
    assert response.status_code == 302
```

### Request Factory

Django request factory for creating request objects for direct view testing.

```python { .api }
def rf() -> django.test.RequestFactory:
    """
    Django request factory for creating request objects.
    
    Creates request objects that can be passed directly to view functions
    for unit testing. Unlike the test client, this doesn't run middleware
    or URL resolution - it creates raw HttpRequest objects.
    
    Returns:
        django.test.RequestFactory: Request factory instance
    """
```

Usage example:
```python
def test_view_directly(rf):
    from myapp.views import my_view
    
    # Create GET request
    request = rf.get("/fake-path/")
    response = my_view(request)
    assert response.status_code == 200
    
    # Create POST request with data
    request = rf.post("/fake-path/", {"name": "test"})
    request.user = user_instance  # Manually set user if needed
    response = my_view(request)
    assert response.status_code == 302
```

### Async Request Factory

Async version of Django request factory for testing async views directly.

```python { .api }
def async_rf() -> django.test.AsyncRequestFactory:
    """
    Django async request factory for creating async request objects.
    
    Creates async request objects for directly testing async view functions
    without running through ASGI middleware stack. Useful for unit testing
    async views in isolation.
    
    Returns:
        django.test.AsyncRequestFactory: Async request factory instance
    """
```

Usage example:
```python
import pytest

@pytest.mark.asyncio
async def test_async_view_directly(async_rf):
    from myapp.views import my_async_view
    
    # Create async GET request
    request = async_rf.get("/fake-path/")
    response = await my_async_view(request)
    assert response.status_code == 200
    
    # Create async POST request
    request = async_rf.post("/fake-path/", {"data": "test"})
    response = await my_async_view(request)
    assert response.status_code == 201
```

## Client Types

```python { .api }
from django.test import Client, AsyncClient, RequestFactory, AsyncRequestFactory
from django.http import HttpRequest, HttpResponse
from typing import Dict, Any, Optional, Union

# HTTP method types
HttpMethodData = Dict[str, Any]
ContentType = str
StatusCode = int

# Response types from Django
from django.http import (
    HttpResponse,
    HttpResponseRedirect,
    HttpResponsePermanentRedirect,
    JsonResponse,
    StreamingHttpResponse,
    FileResponse
)

# Request object types
class HttpRequest:
    """Django HTTP request object."""
    method: str
    path: str
    GET: Dict[str, str]
    POST: Dict[str, str]
    FILES: Dict[str, Any]
    COOKIES: Dict[str, str]
    META: Dict[str, str]
    user: Any
    session: Dict[str, Any]

# Test client method signatures
class Client:
    def get(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpResponse: ...
    def post(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    def put(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    def patch(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    def delete(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    def head(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpResponse: ...
    def options(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    def trace(self, path: str, **extra) -> HttpResponse: ...
    def login(self, **credentials) -> bool: ...
    def logout(self) -> None: ...
    def force_login(self, user, backend: Optional[str] = None) -> None: ...

class AsyncClient:
    async def get(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpResponse: ...
    async def post(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    async def put(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    async def patch(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    async def delete(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    async def head(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpResponse: ...
    async def options(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpResponse: ...
    async def trace(self, path: str, **extra) -> HttpResponse: ...
    async def login(self, **credentials) -> bool: ...
    async def logout(self) -> None: ...
    async def force_login(self, user, backend: Optional[str] = None) -> None: ...

class RequestFactory:
    def get(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpRequest: ...
    def post(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def put(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def patch(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def delete(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def head(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpRequest: ...
    def options(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def trace(self, path: str, **extra) -> HttpRequest: ...

class AsyncRequestFactory:
    def get(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpRequest: ...
    def post(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def put(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def patch(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def delete(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def head(self, path: str, data: Optional[HttpMethodData] = None, **extra) -> HttpRequest: ...
    def options(self, path: str, data: Optional[HttpMethodData] = None, content_type: Optional[ContentType] = None, **extra) -> HttpRequest: ...
    def trace(self, path: str, **extra) -> HttpRequest: ...
```