Wanneer je een Django-app maakt, wordt er automatisch een tests.py aangemaakt.
De meeste ontwikkelaars denken daar op een gegeven moment over na.

“Kan ik dit bestand… verwijderen?”
“Hoe moet ik het eigenlijk gebruiken?”

Eigenlijk is tests.py het officieel aanbevolen testpunt door Django, en,
als je er een beetje aan gewend raakt, biedt het echt een grote verzekering bij refactoren, het toevoegen van functies en upgrades. (Django Project)

In dit artikel bespreken we:

  • Wat het doel van tests.py is

  • Hoe je het schrijft en uitvoert

  • In welke situaties het vooral nuttig is

  • Praktische voorbeelden

Alles in één overzicht.


1. Wat is tests.py voor een bestand?



Django maakt gebruik van het testframework gebaseerd op unittest van de Python standaardbibliotheek.

  • Je maakt testklassen die overerven van django.test.TestCase, en

  • schrijft methoden die beginnen met test_ 

  • met het commando ./manage.py test worden ze automatisch gevonden en uitgevoerd.

Wanneer je een app aanmaakt met startapp, wordt er standaard een tests.py aangemaakt:

  • Kleine projecten → één tests.py is voldoende

  • Bij grotere projecten → aanbevolen door Django om te splitsen in een tests/-pakket (test_models.py, test_views.py, enz.)

Met andere woorden, het is te begrijpen als de basislocatie voor tests op app-niveau.


2. Wanneer is het nuttig? (Waarom zou je tests moeten gebruiken?)

Django-tests komen vooral tot hun recht in de volgende situaties.

  1. Validatie van modellen/businesslogica

    • Hoe ingewikkelder de rekenlogica, statuswijzigingen en aangepaste methoden zijn

    • Als je eenmaal een test hebt gemaakt, kun je erop vertrouwen dat je kunt refactoren bij toekomstige aanpassingen

  2. Weergave/URL/toegangscontrole

    • Verandert de responscode afhankelijk van de authenticatie?

    • Maakt het correct een redirect / foutafhandeling voor onjuiste input?

    • Worden de benodigde contextgegevens voor de sjabloon correct overgedragen?

  3. Voorkomen van regressiefouten

    • Als je een fout eenmaal hebt vastgelegd, kun je die met tests vastleggen, zodat hetzelfde probleem zich niet opnieuw voordoet
  4. Beveiligen van stabiliteit tijdens upgrades

    • Wanneer je de versie van Django of een bibliotheek verhoogt

    • kun je een ./manage.py test draaien om te controleren voordat je verder gaat


3. De meest basale tests.py voorbeeld



3-1. Modeltest

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

class ArticleModelTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="Testtitel",
            content="Inhoud",
            views=0,
        )

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

        # wanneer
        self.article.increase_views()

        # dan
        self.assertEqual(self.article.views, 1)
  • TestCase is een klasse voor Django die unittest.TestCase uitbreidt.

  • Elke test is geïsoleerd per transactie, waardoor de database bij elke test wordt teruggedraaid, zodat ze elkaar niet beïnvloeden.


4. Hoe test je weergave/URL – gebruik van Client

Django biedt een HTTP-client voor testdoeleinden, django.test.Client.
Je kunt GET/POST verzoeken naar een URL sturen en de reacties controleren zonder een echte server te draaien.

4-1. Eenvoudige weergavetest

# 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="Bericht 1",
            content="Inhoud",
            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, "Bericht 1")

Hier zijn de kernpunten:

  • self.client : een testclientinstantie die standaard door TestCase wordt geleverd

  • reverse("article_detail", args=[...]) : in plaats van hardcoding van de URL, ophalen op naam

  • assertContains : controleren of een specifieke string in de respons inhoud is opgenomen

4-2. Test van inloggen/toegangscontrole

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="geheim",
            content="Geheime Inhoud",
            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)
  • Als een anonieme gebruiker toegang probeert te krijgen, wordt er een redirect uitgevoerd

  • Kan een ingelogde gebruiker daarentegen normaal toegang krijgen

Zo kun je buggerelateerde toegangsproblemen aanvankelijk opsporen.


5. Wanneer / hoe voer je tests.py uit?

5-1. Voer alle tests uit

$ python manage.py test
  • Het zoekt en voert automatisch test*.py bestanden uit binnen de huidige directory
    unittest.TestCase of django.test.TestCase subclasses.

5-2. Voer alleen specifieke app uit

$ python manage.py test myapp

5-3. Voer alleen bepaalde modules / klassen / methoden uit

# Hele myapp.tests module
$ python manage.py test myapp.tests

# Specifieke TestCase
$ python manage.py test myapp.tests.ArticleViewTests

# Bepaalde testmethode
$ python manage.py test myapp.tests.ArticleViewTests.test_article_detail_page_returns_200

Het is goed om tijdens de ontwikkeling de gewoonte aan te nemen om alleen de gewijzigde delen snel opnieuw uit te voeren.


6. tests.py uitbreiden naar tests-pakket

Als het project groeit, kan één tests.py bestand niet langer voldoende zijn.
Django-documentatie raadt ook aan om het in pakketstructuur te splitsen.(Django Project)

Bijvoorbeeld:

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

In elk bestand:

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

class ArticleModelTests(TestCase):
    ...

Je kunt het op deze manier splitsen en het uitvoeren blijft hetzelfde.

$ python manage.py test myapp   # Alles
$ python manage.py test myapp.tests.test_models  # Alleen modeltesten

7. Enkele praktische gebruiksvoorbeelden

7-1. “Bug reproductietests” voor regressie voorkomen

  1. Er treedt een bug op in productie

  2. Schrijf een reproductietest lokaal (deze moet falen)

  3. Pas de code aan

  4. Controleer of de test slaagt

  5. git met tests + correctiecode samen commit

Als je dit doet, voorkom je dat dezelfde bug opnieuw optreedt tijdens refactoring.

7-2. API-response-indeling vastzetten

Bij samenwerking met de frontend is het formaat van de API-respons heel belangrijk.

class ArticleApiTests(TestCase):
    def setUp(self):
        self.article = Article.objects.create(
            title="API Titel",
            content="Inhoud",
            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)
  • Geef expliciet aan welke velden altijd moeten worden opgenomen in de respons

  • Als je een veldnaam per ongeluk wijzigt of verwijdert, zal de test onmiddellijk falen

7-3. Testen van formuliervalidatielogica

from .forms import ArticleForm

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

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

    def test_valid_form(self):
        form = ArticleForm(data={"title": "Titel", "content": "Inhoud"})

        self.assertTrue(form.is_valid())
  • Je kunt de validatieregels van het formulier als documentatie met testen vastleggen.

Django dev schrijven test py

8. Samenvatting – Verwijder tests.py niet, maar gebruik het een keer goed

In één zin samengevat:

tests.py is het bestand dat een “veiligheidsnet voor Django-apps” creëert.

  • In het begin kan het vervelend zijn, maar

  • Als je eenmaal de flow van ontwikkelen op basis van tests hebt ervaren,

  • zal de stress bij refactoren/functie toevoegingen/upgrades aanzienlijk verminderen.

In echte projecten:

  1. Bij het maken van nieuwe functies – voeg minimaal 1-2 tests toe

  2. Bij het herstellen van bugs – schrijf altijd eerst een test die de bug reproduceert

  3. Wanneer je begint met het uitbreiden van het project – refactor tests.py naar een tests/ pakket

Als je dit patroon volgt, zul je een totaal andere ontwikkelervaring hebben dan met een “Django-project zonder tests”.