Python is dynamically typed language
so
let's execute the code before running on production
but
tests don't replace static typing
<< Missing Gary Bernhardt PyCon US 2015 keynote >>
We are programmers
so
let's automate
Read more here:
http://martinfowler.com/bliki/UnitTest.html
Read more here:
http://stackoverflow.com/questions/96297/what-are-some-popular-naming-conventions-for-unit-tests
[name of unit being tested]Tests
class LoginViewTests(unittest.TestCase):
pass
test_[feature being tested]_[expected behaviour]
def test_post_should_login_user(self):
pass
Read more here:
http://martinfowler.com/bliki/GivenWhenThen.html
def test_post_should_set_new_username(self):
# Given
# When
# Then
def test_post_should_set_new_username(self):
user = create_user()
data = {
'new_username': 'ringo',
'current_password': 'secret',
}
request = self.factory.post(user=user, data=data)
response = self.view(request)
self.assert_status_equal(response, status.HTTP_200_OK)
user = utils.refresh(user)
self.assertEqual(data['new_username'], user.username)
Given
When
Then
Empty lines between sections
@mock.patch('stripe.Charge.create')
def test_post_should_return_error(self, stripe_create_mock):
stripe_create_mock.side_effect = stripe.StripeError()
lead_offer = factories.LeadOfferFactory()
request = self.factory.post(data={'token': 'wrong-token'})
response = self.view(request, slug=lead_offer.slug)
self.assert_status_equal(response, status.HTTP_400_BAD_REQUEST)
self.assert_emails_in_mailbox(0)
unittest.mock in Python 3
Django fixtures - don't do it
class UserFactory(factory.DjangoModelFactory):
FACTORY_FOR = auth.get_user_model()
email = factory.Sequence(lambda n: 'user-{0}@example.com'.format(n))
password = 'pass'
@classmethod
def _prepare(cls, create, **kwargs):
password = kwargs.pop('password', None)
user = super(UserFactory, cls)._prepare(create, **kwargs)
if password:
user.raw_password = password
user.set_password(password)
if create:
user.save()
return user
# Don't hit database
user = UserFactory.build()
# Hit database
user = UserFactory.create()
django.tests.TestCase
django.tests.Client
django.tests.RequestFactory
Star please!
class YourViewTest(assertions.StatusCodeAssertionsMixin,
assertions.MessagesAssertionsMixin,
testcases.ViewTestCase):
view_class = YourView
view_kwargs = {'some_kwarg': 'value'}
middleware_classes = [
SessionMiddleware,
(MessageMiddleware, testcases.MiddlewareType.PROCESS_REQUEST),
]
def test_post_should_redirect_and_add_message_when_next_parameter(self):
user = UserFactory()
request = self.factory.post(data={'next': '/'}, user=user)
response = self.view(request)
self.assert_redirect(response, '/')
self.assert_message_exists(request, messages.SUCCESS, 'Success!')
class TestModel(models.Model):
field = models.CharField(max_length=100)
file = models.FileField(upload_to='files')
class CreateAPIView(generics.CreateAPIView):
model = models.TestModel
class CreateAPIViewTest(assertions.StatusCodeAssertionsMixin,
files.InMemoryStorageMixin,
restframework.APIViewTestCase):
view_class = CreateAPIView
def test_post_should_create_model(self):
data = {
'field': 'test value',
'file': files.create_inmemory_file('test.txt', content=b'Hello multipart!'),
}
request = self.factory.post(data=data, format='multipart')
response = self.view(request)
self.assert_status_equal(response, status.HTTP_201_CREATED)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
}
}
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
}
}