Al crear una aplicación Django, se genera automáticamente un tests.py.
La mayoría de los desarrolladores han pensado al menos una vez:

“¿Este archivo... se puede borrar?”
“¿Cómo se supone que debo usarlo?”

De hecho, tests.py es el punto de entrada para pruebas recomendado oficialmente por Django, y
una vez que te acostumbras un poco, se convierte en una gran aseguradora durante refactorizaciones, adiciones de funciones y actualizaciones de versión. (Proyecto Django)

En este artículo:

  • qué es el archivo tests.py

  • cómo escribirlo y ejecutarlo

  • en qué situaciones es especialmente útil

  • ejemplos de uso en la práctica

lo organizamos todo de una vez.


1. ¿Qué es el archivo tests.py?



Django utiliza un framework de pruebas basado en unittest de la biblioteca estándar de Python.

  • Creando una clase de prueba que hereda de django.test.TestCase y

  • escribiendo métodos que comienzan con test_ dentro de ella,

  • se ejecutan automáticamente con el comando ./manage.py test.

Cuando creas una aplicación con startapp, se genera un tests.py de forma predeterminada:

  • En proyectos pequeños → un solo tests.py es suficiente

  • Si el proyecto crece → se recomienda gestionarlo dividiéndolo en un paquete tests/ (test_models.py, test_views.py, etc.) en la documentación de Django.

En otras palabras, se puede entender como “la ubicación básica para pruebas a nivel de aplicación”.


2. ¿Cuándo es útil? (¿Por qué deberíamos usar pruebas?)

Las pruebas en Django brillan especialmente en las siguientes situaciones.

  1. Validación de modelos/lógica de negocio

    • Cuanto más compleja sea la lógica de cálculo, los cambios de estado o los métodos personalizados,

    • si creas una prueba, puedes realizar refactorizaciones con confianza en futuras modificaciones.

  2. Comprobación de vistas/URLs/permisos

    • Verificar si el código de respuesta varía según la autenticación

    • Comprobar si maneja adecuadamente redirecciones/errores por entradas incorrectas

    • Asegurarse de que el contexto necesario se pase correctamente a las plantillas

  3. Prevención de regresiones

    • Si solucionaste un error, lo fijas con una prueba para que el mismo problema no vuelva a surgir.
  4. Asegurar estabilidad durante actualizaciones de versiones

    • Cuando actualizas Django o bibliotecas,

    • puedes ejecutar ./manage.py test antes de continuar.


3. Ejemplo más básico de tests.py



3-1. Prueba de modelo

# myapp/tests.py
from django.test import TestCase
from .models import Article

class ArticleModelTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Título de prueba",
            content="Contenido",
            views=0,
        )

    def test_increase_views(self):
        # dado
        self.assertEqual(self.article.views, 0)

        # cuando
        self.article.increase_views()

        # entonces
        self.assertEqual(self.article.views, 1)
  • TestCase es una clase de Django que extiende unittest.TestCase.

  • Cada prueba se ejecuta de forma aislada en unidades de transacción, y la base de datos se revierte entre pruebas para evitar interferencias.


4. Cómo probar vistas/URLs – Usando Client

Django proporciona un cliente HTTP de pruebas django.test.Client.
Puedes enviar solicitudes GET/POST a URLs y comprobar las respuestas sin levantar un servidor real.

4-1. Prueba de vista simple

# myapp/tests.py
from django.test import TestCase
from django.urls import reverse
from .models import Article

class ArticleViewTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Artículo 1",
            content="Contenido",
            views=0,
        )

    def test_article_detail_page_returns_200(self):
        url = reverse("article_detail", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "Artículo 1")

Los puntos clave son:

  • self.client: la instancia del cliente de prueba que se proporciona por defecto en TestCase

  • reverse("article_detail", args=[...]): en lugar de codificar la URL, busca por el nombre de la URL.

  • assertContains: comprueba si una cadena específica está incluida en el cuerpo de la respuesta.

4-2. Pruebas de verificación de inicio de sesión/permisos

from django.contrib.auth import get_user_model

class ArticlePermissionTests(TestCase):
    def setUp(self):
        self.user = get_user_model().objects.create_user(
            username="tester",
            password="pass1234",
        )
        self.article = Article.objects.create(
            title="secreto",
            content="Mensaje secreto",
            views=0,
        )

    def test_anonymous_user_redirected_to_login(self):
        url = reverse("article_edit", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 302)
        self.assertIn("/accounts/login", response["Location"])

    def test_logged_in_user_can_access_edit_page(self):
        self.client.login(username="tester", password="pass1234")
        url = reverse("article_edit", args=[self.article.id])

        response = self.client.get(url)

        self.assertEqual(response.status_code, 200)
  • verificamos si se redirige a los usuarios anónimos

  • si los usuarios que han iniciado sesión pueden acceder correctamente

Se pueden identificar errores relacionados con permisos desde el principio.


5. ¿Cuándo/ cómo ejecutar tests.py?

5-1. Ejecutar todas las pruebas

$ python manage.py test
  • Busca y ejecuta automáticamente desde archivos test*.py en el directorio actual
    subclases de unittest.TestCase o django.test.TestCase.

5-2. Ejecutar solo una aplicación específica

$ python manage.py test myapp

5-3. Ejecutar solo un módulo/clase/método específico

# todo el módulo myapp.tests
$ python manage.py test myapp.tests

# caso de prueba específico
$ python manage.py test myapp.tests.ArticleViewTests

# método de prueba específico
$ python manage.py test myapp.tests.ArticleViewTests.test_article_detail_page_returns_200

Es bueno tener el hábito de ejecutar rápidamente solo las partes modificadas durante el desarrollo.


6. Ampliando tests.py a un paquete de pruebas

A medida que el proyecto crece, tests.py solo no es suficiente.
La documentación de Django también recomienda dividirlo en una estructura de paquetes. (Proyecto Django)

Ejemplo:

myapp/
    tests/
        __init__.py
        test_models.py
        test_views.py
        test_forms.py
        test_api.py

Dentro de cada archivo:

# myapp/tests/test_models.py
from django.test import TestCase
from myapp.models import Article

class ArticleModelTests(TestCase):
    ...

Se puede dividir así y la ejecución sigue siendo la misma.

$ python manage.py test myapp   # todo
$ python manage.py test myapp.tests.test_models  # solo pruebas de modelo

7. Algunos casos de uso en la práctica

7-1. Usar pruebas para “reproducir errores” y evitar regresiones

  1. Error en producción

  2. Escribir una prueba para reproducir en local (debe fallar para ser correcto)

  3. Modificar el código

  4. Confirmar que la prueba tiene éxito

  5. Agregar a git la prueba y el código modificado juntos

De esta manera, puedes asegurarte de que no aparezcan los mismos errores en futuras refactorizaciones.

7-2. Fijar el formato de respuesta de la API

Al colaborar con el frontend, el formato de respuesta de la API es muy importante.

class ArticleApiTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Título de API",
            content="Contenido",
            views=0,
        )

    def test_article_detail_api_response(self):
        url = reverse("api:article-detail", args=[self.article.id])

        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)

        data = response.json()
        self.assertEqual(data["id"], self.article.id)
        self.assertIn("title", data)
        self.assertIn("content", data)
  • Especificar qué campos deben incluirse en la respuesta.

  • Si se cambian o eliminan accidentalmente los nombres de los campos, el test fallará inmediatamente.

7-3. Pruebas de la lógica de validación de formularios

from .forms import ArticleForm

class ArticleFormTests(TestCase):
    def test_title_is_required(self):
        form = ArticleForm(data={"title": "", "content": "Contenido"})

        self.assertFalse(form.is_valid())
        self.assertIn("title", form.errors)

    def test_valid_form(self):
        form = ArticleForm(data={"title": "Título", "content": "Contenido"})

        self.assertTrue(form.is_valid())
  • Puedes dejar las reglas de validación del formulario documentadas como pruebas.

Django dev escribiendo test py

8. Conclusión – No borres tests.py y prueba a usarlo correctamente una vez

En resumen:

El tests.py es el archivo que crea “la red de seguridad de la aplicación Django”.

  • Al principio es tedioso, pero,

  • una vez que experimentas el flujo de desarrollo basado en pruebas,

  • el estrés durante refactorizaciones, adiciones de funciones y actualizaciones de versión se reduce considerablemente.

En un proyecto real:

  1. Cuando crees nuevas funcionalidades – agrega al menos 1 o 2 pruebas al mismo tiempo.

  2. Cuando corrijas errores – asegúrate de primero escribir la prueba que reproduce ese error.

  3. Cuando comiences a hacer crecer el proyecto – refactoriza tests.py a un paquete tests/.

Si sigues este patrón, experimentarás un desarrollo completamente diferente al de un “proyecto Django sin pruebas”.