Testowanie aplikacji Django

Przegląd możliwości testowania aplikacji w Django.

Testowanie kodu umożliwia szybkie wykrywanie błędów, a także kontrolę poprawnego funkcjonowania aplikacji. Przy wprowadzaniu zmian do kodu za pomocą testów sprawdzimy czy wprowadzone zmiany nie spowodowały błędów w innych częściach aplikacji. Django posiada zestaw narzędzi umożliwiających pisanie testów dla naszych aplikacji. Do dyspozycji mamy dwie metody pisania testów:
  • Doctests - test zawarty do docstringu funkcji i emulujący "zachowanie" interaktywnego interpretera Pythona:
    def my_func(a_list, idx):
        """
        >>> a = ['larry', 'curly', 'moe']
        >>> my_func(a, 0)
        'larry'
        >>> my_func(a, 1)
        'curly'
        """
        return a_list[idx]
    
  • Testy jednostkowe (Unit tests) - metody klasy dziedziczącej unittest.TestCase
    import unittest
    
    class MyFuncTestCase(unittest.TestCase)
        def testBasic(self):
            a = ['larry', 'curly', 'moe']
            self.assertEquals(my_func(a, 0), 'larry')
            self.assertEquals(my_func(a, 1), 'curly')
    

Docstringi

PEP 257 opisuje zasadę tworzenia doctestów. Django szuka takich testów w pliku models.py aplikacji, oraz pliku w tests.py jeżeli istnieje (w katalogu aplikacji):
# models.py

from django.db import models

class Animal(models.Model):
    """
    Zwierzęta wydające odgłosy

    # Tworzymy zwierzęta
    >>> lew = Animal.objects.create(name="lew", sound="ryk")
    >>> kot = Animal.objects.create(name="kot", sound="mial")

    # Niech powiedzą
    >>> lew.speak()
    'lew mówi "ryk"'
    >>> kot.speak()
    'kot mówi "mial"'
    """
    name = models.CharField(max_length=20)
    sound = models.CharField(max_length=20)

    def speak(self):
        return '%s mówi "%s"' % (self.name, self.sound)
Przy wykonywaniu testów django znajdzie fragment wyglądający jak kod z sesji interpretera i wykona go porównując wynik z oczekiwaną wartością. Dodatkowo stworzona zostanie oddzielna baza danych dla samych testów, tak wiec testowanie aplikacji nie wpłynie na produkcyjną bazę danych.

Testy jednostkowe

Django szuka testów jednostkowy w pliku models.py szukając klas dziedziczących unittest.TestCase, a także w pliku tests.py także szukając klas dziedziczących unittest.TestCase. Przykład:
import unittest
from myapp.models import Animal

class AnimalTestCase(unittest.TestCase):
    def setUp(self):
        self.lew = Animal.objects.create(name="lew", sound="ryk")
        self.kot = Animal.objects.create(name="kot", sound="mial")

    def testSpeaking(self):
        self.assertEquals(self.lew.speak(), 'lew mówi "ryk"')
        self.assertEquals(self.kot.speak(), 'kot mówi "mial"')

Wykonywanie testów

By wykonać wszystkie testy wystarczy polecenie:
manage.py test
Można także wykonać testy dla danej aplikacji:
manage.py test NAZWA_APLIKACJI

Klient testów

test.client to klasa Pythona udająca prostą przeglądarkę. Umożliwia wykonywanie żądań GET i POST pobierając otrzymane dane. Umożliwia analizę jakie szablony i widoki zostały wykonane dla danego adresu URL, a także jakie dane zostały zwrócone. Przykład:
>>> from django.test.client import Client
>>> c = Client()
>>> response = c.post('/login/', {'username': 'john', 'password': 'smith'})
>>> response.status_code
200
>>> response = c.get('/customer/details/')
>>> response.content
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 ...'
Klient testów pobiera daną stronę korzystając z pomocy URLconf, a nie wysyła żądań HTTP. Pełen opis możliwości dostępny jest w dokumentacji Django. Oto przykład testu jednostkowego wykorzystującego klienta testów:
import unittest
from django.test.client import Client

class SimpleTest(unittest.TestCase):
    def setUp(self):
        # Every test needs a client.
        self.client = Client()

    def test_details(self):
        # Issue a GET request.
        response = self.client.get('/customer/details/')

        # Check that the respose is 200 OK.
        self.failUnlessEqual(response.status_code, 200)

        # Check that the rendered context contains 5 customers.
        self.failUnlessEqual(len(response.context['customers']), 5)

TestCase

Django posiada rozbudowaną wersję unittest.TestCase wzbogaconą o klienta testów i kilka innych funkcjonalności. Zamiast kodu:
import unittest
from django.test.client import Client

class SimpleTest(unittest.TestCase):
    def test_details(self):
        client = Client()
        response = client.get('/customer/details/')
        self.failUnlessEqual(response.status_code, 200)

    def test_index(self):
        client = Client()
        response = client.get('/customer/index/')
        self.failUnlessEqual(response.status_code, 200)
Wystarczy taki:
from django.test import TestCase

class SimpleTest(TestCase):
    def test_details(self):
        response = self.client.get('/customer/details/')
        self.failUnlessEqual(response.status_code, 200)

    def test_index(self):
        response = self.client.get('/customer/index/')
        self.failUnlessEqual(response.status_code, 200)
Dokumentacja Django dotycząca testów
blog comments powered by Disqus

Kategorie

Strony