Testy


Code without tests is broken by design.
Jacobian

Rodzaje testów

  • integracyjne
  • systemowe
  • akceptacyjne
  • regresji
  • jednostkowe

Testy ogólnie

from django.test.testcases import TestCase
from example.models import Author


class AuthorTestCase(TestCase):

    def test_method_get_full_name(self):
        author = Author(first_name='Marian', last_name='Opania')

        full_name = author.get_full_name()

        self.assertEqual(full_name, 'Marian Opania')

Co testować?

 class CreateAuthorView(CreateView):
     model = Author
     form_class = AuthorForm
     def form_valid(self, form):
         messsages.success(self.request, 'Added author!')
         return super(CreateAuthorView, self).form_valid(form)
     def get_context_data(self, **kwargs):
         context = super(CreateAuthorView, self).get_context_data(**kwargs)
         context['my_little_pony'] = PonyFactory(author=self.object)
         return context

Nie testujemy

  1. Wbudowanych funkcji Pythona.
  2. Django.
  3. Czystej konfiguracji.

Testujemy

Całą resztę

Przygotuj

(ang. given/arrange)

Przygotuj dane

  1. Nie używamy fixtur.
  2. Tworzymy obiekty kiedy są potrzebne.
  3. Staramy się nie dotykać bazy.
  4. Używamy fabryk obiektów (np. model_mommy).
 class AuthorTestCase(TestCase):

    def test_get_full_name_with_db_call(self):
        author = Author.objects.create(first_name='Marian', last_name='Opania')

        full_name = author.get_full_name()

        self.assertEqual(full_name, 'Marian Opania')
def test_get_full_name_with_no_db_calls(self): author = Author(first_name='Marian', last_name='Opania')
def test_get_full_name_with_mommy(self): author = mommy.prepare(Author, first_name='Marian', last_name='Opania')

Przygotuj "okoliczności"

class AuthorDetailViewTestCase(TestCase):

    def setUp(self):
        self.author = mommy.make(Author)
        self.view = AuthorDetailView.as_view()
# a lot of tests here def tearDown(self): self.author.remove_some_author_stuff_from_outside_db()


class ElasticsearchTestCase(TestCase): index_name = 'test' def setUpClass(self): es.indices.remove(self.index_name) es.indices.create(self.index_name) def tearDownClass(self): es.indices.remove(self.index_name)

Zamockuj!

Mock to bardzo rozbudowana biblioteka, ale warto jej używać!
    def test_form_valid_should_return_proper_info(self):
        form = mock.Mock()
        self.view.request = mock.Mock()
        self.view.survey = mock.Mock(captcha_verification__return_value=False)
        self.view.success_message = 'success'
        self.view.render_to_response = lambda x: x

        context = self.view.form_valid(form)

        self.assertEqual(context['message'], 'success')
        self.assertTrue(context['clearform'])
        self.view.survey.responded.assert_called_once_with(context)
     

Podsumowanie

  1. Nie nadużywaj setUp i setUpClass.
  2. Pisz własne funkcje wołane w testach.
  3. Używaj biblioteki mock.
  4. Pamiętaj o dekoratorze override_settings.
  5. Przygotowanie nie powinno być bolesne!

Zrób

(ang. when/act)

Dobre przykłady

 template_name = view.get_template_name()
response = self.view.get(request)
with self.assertRaises(Http404): self.view.get(request)

Z reguły więc jest to wywołanie pojedynczej, testowanej funkcji, która została wcześniej maksymalnie odizolowana od innych.

Sprawdź

(ang. then/assert)

Asercje i nie tylko

class ConfirmResponseViewTestCase(K2TestCase):
    view_class = ConfirmResponseView

    def test_response_should_be_confirmed_and_time_should_be_set(self):
        before = datetime.datetime.now()
        response = mommy.make(models.Response, confirmed=False)
        request = self.request_factory.get('')
        self.view.request = request
        self.view.get(request=request)

        refreshed_response = models.Response.objects.get(id=response.id)
        self.assertTrue(refreshed_response.confirmed)
        self.assertGreaterEqual(refreshed_response.time_confirmed, before)

Wnioski

  1. Asercji nie powinno być za wiele.
  2. Można pisać własne asercje!
  3. Warto czasem użyć self.fail()





Twój test powinien
być jak dobre haiku
tak samo prosty

Mistrz Karimata

Egzegeza haiku Mistrza Karimaty


Czasami zdarza się tak, że musimy napisać dużo dziwnych rzeczy, żeby coś przetestować, a sam test staje się długi i nieczytelny.


To znak, że najwyraźniej nie testujemy czegoś
naprawdę jednostkowo.


Niewykluczone, że to sam kod wymaga zmiany i lepszego podziału!

Testy w Django

problemy i wyzwania ;)

Unit test?

from django.test import TestCase

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

Unit test? Nie!

Przetestowaliśmy:
  1. Urlresolvera i nasze ustawienia w urls.py
  2. Wszelkie dekoratory (login_required, etc.)
  3. Wszystkie middlewary.
  4. Renderowanie szablonu, template tagi.
  5. Sam widok.

Testowanie widoków klasowych 1

Używajmy RequestFactory i TemplateResponse.
from django.test import TestCase, RequestFactory

class SimpleTest(TestCase): def setUp(self): self.factory = RequestFactory() def test_details(self): request = self.factory.get('/customer/details') response = my_view(request) self.assertEqual(response.status_code, 200) self.assertIn('customer', response.context_data)

Testowanie widoków klasowych 2

Testujmy jednostkowo pojedyncze metody widoku!
Musimy sami "przygotować" magię, która normalnie dzieje się w metodzie as_view.
     def test_single_view_method(self):
        request = self.factory.get('/')
        self.view.request = request
        self.view.kwargs = {
            'code': 'small code',
        }

        result = self.view.method_to_test()

        self.assertEqual(result, 'big code')

Inne problemy

  1. Pliki zostające między testami.
  2. Skąd brać pliki do testów?

Zapamiętaj

  1. Testowanie wcale nie boli!
  2. ... a jeżeli boli, to nie jest to wina testów, tylko kodu.
  3. Test jednostkowy dobrze izoluje testowany kod.
  4. Testy jednostkowe to większość, ale nie wszystkie testy.

Napisz test!

testy

By Szymon Teżewski

testy

  • 2,350