Showing posts with label unittest. Show all posts
Showing posts with label unittest. Show all posts

Jul 30, 2012

Python 3.1 unittest2 framework new features

skip, skipIf and skipUnless decorators
class MyTestCase(unittest.TestCase):

    @unittest.skip("demonstrating skipping")
    def test_nothing(self):
        self.fail("shouldn't happen")

    @unittest.skipIf(mylib.__version__ < (1, 3),
                     "not supported in this library version")
    def test_format(self):
        # Tests that work for only a certain version of the library.
        pass

    @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
    def test_windows_support(self):
        # windows specific testing code
        pass
expectedFailure decorator
class ExpectedFailureTestCase(unittest.TestCase):
    @unittest.expectedFailure
    def test_fail(self):
        self.assertEqual(1, 0, "broken")

Jun 18, 2012

Apr 16, 2012

What type of test to write?

  • Write system tests for your views.
  • Write Selenium tests for Ajax, other JS/server interactions.
  • Write unit tests for everything else (not strict).
  • Test each case (code branch) where it occurs.
  • One assert/action per test case method.

Change settings in tests

@override_settings(ALLOW_COMMENTS=True)
def test_comments_allowed(self):
  # ...
https://github.com/carljm/django-testing-slides/blob/master/settings/30_good.md

Imposing no-DB discipline

from django.utils.unittest import TestCase

import mock

cursor_wrapper = mock.Mock()
cursor_wrapper.side_effect = RuntimeError("No touching the database!")


@mock.patch("django.db.backends.util.CursorWrapper", cursor_wrapper)
class NoDBTestCase(TestCase):
    """Will blow up if you database."""
https://github.com/carljm/django-testing-slides/blob/master/models/30_no_database.md

Selenium first test

http://habrahabr.ru/post/141994/

Лучше использовать Webdriver а не Selenium RC

Apr 13, 2012

Testing models, forms

Form
Django Forms Deep Dive - Nathan R. Yergler
import unittest

class FormTests(unittest.TestCase):
    def test_validation(self):
        form_data = {
            'name': 'X' * 300,
        }

        form = ContactForm(data=form_data)
        self.assertFalse(form.is_valid())
Eventbrite just released a library on GitHub called rebar – https://github.com/eventbrite/rebar
from rebar.testing import flatten_to_dict

form_data = flatten_to_dict(ContactForm())
form_data.update({
        'name': 'X' * 300,
    })
form = ContactForm(data=form_data)
assert(not form.is_valid())
ModelForm
class ModelFormTests(unittest.TestCase):
    def test_validation(self):
        form_data = {
            'name': 'Test Name',
        }

        form = ContactForm(data=form_data)
        self.assert_(form.is_valid())
        self.assertEqual(form.instance.name, 'Test Name')

        form.save()

        self.assertEqual(
            Contact.objects.get(id=form.instance.id).name,
            'Test Name'
        )
FormSet
from rebar.testing import flatten_to_dict, empty_form_data

formset = ContactFormSet()
form_data = flatten_to_dict(formset)
form_data.update(
    empty_form_data(formset, len(formset))
)
Model
Fast test, slow test by Gary Bernhardt
def test_that_spam_posts_are_hidden(self):
    post = Post(mark_post_as_spam=True)
    discussion = Discussion(posts=[post])
    assert discussion.visible_posts == []

Feb 17, 2012

Test Django transaction using mocking and TransactionTestCase

It's pretty simple using mock side_effect:
class MyTransactionTestCase(TransactionTestCase):
    fixtures = ['some_fixture.json',]

    @patch.object(SomeObjectInsideTransaction, 'some_method')
    def test_disable_transaction(self, some_method):
        some_method.side_effect = IOError("Unexpected exception")

        try:
            self.client.get('/your/view/url/')
        except IOError:
            pass

        # assertions here
You have to use TransactionTestCase:
Django TestCase classes make use of database transaction facilities, if available, to speed up the process of resetting the database to a known state at the beginning of each test. A consequence of this, however, is that the effects of transaction commit and rollback cannot be tested by a Django TestCase class. If your test requires testing of such transactional behavior, you should use a Django TransactionTestCase.

Feb 2, 2012

Chaos Python

Add this into your functional tests and smoke it.
import sys, random

def chaos_trace(frame, event, arg): 
   if event == 'line' and random.random() < 0.000001: 
       raise MemoryError()
   return chaos_trace

sys.settrace(chaos_trace)
You will get some lovely random failures injected into your code. A great way to find bugs, and make sure your reasoning is sound in the face of CHAOS!

Jan 20, 2012

Mocking

Mock basics

mock = Mock(name='bar', return_value='fish')
mock(1, 2, spam = 99)
# 'fish'
mock.assert_called_once_with(1, 2, spam = 99)
mock.called
# True
mock.call_count
# 1
mock.call_args
# ((1, 2), {'spam': 99})
From: http://blip.tv/pycon-us-videos-2009-2010-2011/pycon-2011-testing-with-mock-4899484

Magic Mock basics

mock = MagicMock()
mock[5]
# <mock.Mock object at 0x2f90690>
mock.__str__.return_value = 'bzzzz'
str(mock)
# 'bzzzz'

Mock exceptions handling test

mock = Mock(side_effect=KeyError('boom'))
mock()
# Traceback (most recent call last)
# ....
# KeyError: 'boom'

Mock file write

>>> open_name = '%s.open' % __name__
>>> with patch(open_name, create=True) as mock_open:
...     mock_open.return_value = MagicMock(spec=file)
...
...     with open('/some/path', 'w') as f:
...         f.write('something')
...

>>> file_handle = mock_open.return_value.__enter__.return_value
>>> file_handle.write.assert_called_with('something')
http://stackoverflow.com/questions/1289894/how-do-i-mock-an-open-used-in-a-with-statement-using-the-mock-framework-in-pyth
http://www.voidspace.org.uk/python/mock/magicmock.html

Mock file and raw_post_data

from mock import Mock, MagicMock

class SomeTestCase(TestCase):

    def testRawPostData(self):
        ...

        request = Mock(spec=request)
        request.raw_post_data = 'myrawdata'
        print request.raw_post_data  # prints 'myrawdata'

        file_mock = MagicMock(spec=file)
        file_mock.read.return_value = 'myfiledata'
        request.FILES = {'myfile': file_mock}    
        print request.FILES['myfile'].read()  # prints 'myfiledata'

Mocking stdout

outstream = StringIO()
with patch('sys.stdout', new=outstream) as out:
    ...
    actual_out = out.getvalue()

Video: 
A Gentle Introduction to Mock for Python
Why Use Mock?
Mock and Django

Nov 10, 2011

Setup module function (UnitTest)

The default ordering of tests created by the unittest test loaders is to group all tests from the same modules and classes together. This will lead to setUpClass / setUpModule (etc) being called exactly once per class and module. If you randomize the order, so that tests from different modules and classes are adjacent to each other, then these shared fixture functions may be called multiple times in a single test run.
import unittest

def setUpModule():
    print 'Module setup...'

def tearDownModule():
    print 'Module teardown...'

class Test(unittest.TestCase):
    def setUp(self):
        print 'Class setup...'

    def tearDown(self):
        print 'Class teardown...'

    def test_one(self):
        print 'One'

    def test_two(self):
        print 'Two'

Oct 28, 2011

Testing applications and extensions

Python Testing Tools Taxonomy
Django Packages: Testing Tools

Open Source Testing
Link Checker

django-testtools - A helper for writting Django's tests.
  • assertRecipients
  • assertQuerySetEqual
  • assertErrorsInForm
django-test-extensions - A set of custom assertions and examples for use testing django applications.
  • login_as_admin
  • ...
  • assert_file_exists
  • assert_key_exists
  • assert_has_attr
  • ...
  • assert_mail
  • assert_latest
  • assert_model_changes
django-autofixture - Can create auto-generated test data
from autofixture import AutoFixture
fixture = AutoFixture(Entry)
entries = fixture.create(10)
django-test-utils
  • Django Testmaker 
  • Django Crawler 
  • Django Test Runner 
  • Twill Runner 
  • Persistent Database Test Runner
+
./manage.py makefixture proposals.Proposal[:10] --indent=4 > proposal_with_related_items.json
factory_boy - A test fixtures replacement for Python based on thoughtbot's factory_girl for Ruby
import factory
from models import User

class UserFactory(factory.Factory):
    FACTORY_FOR = User

    first_name = 'John'
    last_name = 'Doe'
    admin = False

# Returns a User instance that's not saved
user = UserFactory.build()

# Returns a saved User instance
user = UserFactory.create()

# Returns a dict of attributes that can be used to build a User instance
attributes = UserFactory.attributes()

# Returns an object with all defined attributes stubbed out:
stub = UserFactory.stub()
rebar - Rebar makes your Forms stronger
from rebar.testing import flatten_to_dict

form_data = flatten_to_dict(ContactForm())
form_data.update({
        'name': 'X' * 300,
    })
form = ContactForm(data=form_data)
assert(not form.is_valid())
The same for formsets
from rebar.testing import flatten_to_dict, empty_form_data

formset = ContactFormSet()
form_data = flatten_to_dict(formset)
form_data.update(
    empty_form_data(formset, len(formset))
)