Testing de vistas con el Client de Django

Intermedio
Django
Django
Actualizado: 18/04/2026

El Client de test de Django

self.client es un cliente HTTP simulado disponible en todos los TestCase de Django. Permite hacer peticiones GET, POST, PUT, DELETE, etc. sin necesitar un servidor real:

from django.test import TestCase
from django.urls import reverse
from django.contrib.auth import get_user_model
from .models import Producto, Categoria

User = get_user_model()

class ProductoListViewTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.categoria = Categoria.objects.create(nombre='Test', slug='test')
        cls.producto = Producto.objects.create(
            nombre='Laptop Test',
            precio=999.99,
            categoria=cls.categoria,
            activo=True
        )
        cls.url = reverse('catalogo:producto-list')

    def test_lista_accesible_sin_autenticacion(self):
        """El listado de productos debe ser público."""
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 200)

    def test_lista_usa_template_correcto(self):
        response = self.client.get(self.url)
        self.assertTemplateUsed(response, 'catalogo/producto_list.html')

    def test_lista_contiene_producto(self):
        response = self.client.get(self.url)
        self.assertContains(response, 'Laptop Test')

    def test_lista_no_muestra_inactivos(self):
        producto_inactivo = Producto.objects.create(
            nombre='Producto Inactivo',
            precio=10.00,
            categoria=self.categoria,
            activo=False
        )
        response = self.client.get(self.url)
        self.assertNotContains(response, 'Producto Inactivo')

    def test_paginacion(self):
        # Crear muchos productos para activar la paginación
        for i in range(15):
            Producto.objects.create(
                nombre=f'Producto {i}',
                precio=10.00,
                categoria=self.categoria
            )
        response = self.client.get(self.url)
        self.assertIn('is_paginated', response.context)

Diagrama conceptual de Testing de vistas con el Client de Django

Tests de vistas autenticadas

class CrearProductoViewTest(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.categoria = Categoria.objects.create(nombre='Test', slug='test')
        cls.usuario = User.objects.create_user(
            username='testuser',
            password='testpass123'
        )
        cls.usuario_admin = User.objects.create_user(
            username='admin',
            password='admin123',
            is_staff=True
        )
        cls.url = reverse('catalogo:producto-create')

    def test_redirige_si_no_autenticado(self):
        response = self.client.get(self.url)
        self.assertRedirects(response, f'/login/?next={self.url}')

    def test_accesible_para_usuario_autenticado(self):
        self.client.login(username='testuser', password='testpass123')
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, 200)

    def test_crear_producto_post_valido(self):
        self.client.force_login(self.usuario_admin)
        datos = {
            'nombre': 'Nuevo Producto',
            'precio': '49.99',
            'categoria': self.categoria.pk,
        }
        response = self.client.post(self.url, data=datos)
        self.assertEqual(response.status_code, 302)  # Redirección tras éxito
        self.assertTrue(Producto.objects.filter(nombre='Nuevo Producto').exists())

    def test_crear_producto_post_invalido(self):
        self.client.force_login(self.usuario_admin)
        datos = {'nombre': '', 'precio': '-1'}  # Datos inválidos
        response = self.client.post(self.url, data=datos)
        self.assertEqual(response.status_code, 200)  # Re-renderiza el formulario
        self.assertFormError(response, 'form', 'nombre', 'Este campo es obligatorio.')

Assertions principales del Client

# Verificar status code
self.assertEqual(response.status_code, 200)

# Verificar contenido en la respuesta
self.assertContains(response, 'texto esperado')
self.assertContains(response, 'texto', count=3)  # Aparece exactamente 3 veces
self.assertNotContains(response, 'texto no esperado')

# Verificar redirección
self.assertRedirects(response, '/productos/')
self.assertRedirects(response, '/productos/', status_code=302, target_status_code=200)

# Verificar plantillas usadas
self.assertTemplateUsed(response, 'catalogo/producto_list.html')
self.assertTemplateNotUsed(response, 'catalogo/error.html')

# Verificar contexto de la plantilla
self.assertIn('productos', response.context)
self.assertEqual(len(response.context['productos']), 5)

# Verificar errores de formulario
self.assertFormError(response, 'form', 'nombre', 'Este campo es obligatorio.')

Testing de APIs con APIClient (DRF)

from rest_framework.test import APIClient, APITestCase
from rest_framework import status
from django.urls import reverse

class ProductoAPITest(APITestCase):
    @classmethod
    def setUpTestData(cls):
        cls.categoria = Categoria.objects.create(nombre='Test', slug='test')
        cls.usuario = User.objects.create_user(username='api_user', password='pass123')
        cls.url_lista = reverse('producto-list')  # Router de DRF

    def setUp(self):
        self.client = APIClient()

    def test_lista_sin_autenticacion(self):
        response = self.client.get(self.url_lista)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_crear_producto_requiere_autenticacion(self):
        response = self.client.post(self.url_lista, {'nombre': 'Test'})
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_crear_producto_autenticado(self):
        self.client.force_authenticate(user=self.usuario)
        datos = {
            'nombre': 'API Producto',
            'precio': '29.99',
            'categoria': self.categoria.pk,
        }
        response = self.client.post(self.url_lista, data=datos)
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(response.data['nombre'], 'API Producto')

    def test_obtener_token_jwt(self):
        url = reverse('token_obtain_pair')
        response = self.client.post(url, {
            'username': 'api_user',
            'password': 'pass123'
        })
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertIn('access', response.data)
        self.assertIn('refresh', response.data)
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Django es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Django

Explora más contenido relacionado con Django y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Usar self.client para simular peticiones GET y POST a las vistas. Verificar el status code, el contenido y las redirecciones de las respuestas. Usar assertContains, assertRedirects y assertTemplateUsed. Autenticar usuarios en tests con self.client.login() y self.client.force_login(). Testear APIs REST con el APIClient de Django REST Framework.