91

I have a model called Thing with an attribute called name, and I want name to be a char field that's only 3 characters long.

How do I write a test for that?

class TestCase1(TestCase):
    def test1(self):
        thing = Thing(name='1234')

that test should fail. How do I properly write the test so that the test passes when that object fails?

5 Answers 5

167

If you're expecting Thing(name='1234') to raise an exception, there are two ways to deal with this.

One is to use Django's assertRaises (actually from unittest/unittest2):

def mytest(self):
    self.assertRaises(FooException, Thing, name='1234')

This fails unless Thing(name='1234') raises a FooException error. Another way is to catch the expected exception and raise one if it doesn't happen, like this:

def mytest(self):
    try:
        thing = Thing(name='1234')
        self.fail("your message here")
    except FooException:
        pass

Obviously, replace the FooException with the one you expect to get from creating the object with too long a string. ValidationError?

A third option (as of Python 2.7) is to use assertRaises as a context manager, which makes for cleaner, more readable code:

def mytest(self):
    with self.assertRaises(FooException):
        thing = Thing(name='1234')

Sadly, this doesn't allow for custom test failure messages, so document your tests well. See https://hg.python.org/cpython/file/2.7/Lib/unittest/case.py#l97 for more details.

Sign up to request clarification or add additional context in comments.

7 Comments

I use assertRaises quite often.
I do as well, but the syntax takes some getting used to.
in python version 2.6.5, the function signature is different. Copied from python code: def failUnlessRaises(self, excClass, callableObj, *args, **kwargs) and then callableObj(*args, **kwargs). So in this post's example, it would be self.assertRaises(FooException, Thing, name='1234'). I'm not sure why my function signature is different, but that's what works.
Actually, I think it's been that way for some time. I just got the sig backwards. I'll update my answer.
assertRaises can also be used in a with statement like this: with self.assertRaises(FooException): thing = Thing(name='1234')
|
7

I am currently using the expectedFailure decorator from unittest. That works as advertised: Fails when there is no error, passes when there is a failure.

I use expectedFailure to verify that my custom assert-routines actually work and not just rubberstamp everything OK.

import unittest
from django.test import TestCase

class EmojiTestCase(TestCase):

    @unittest.expectedFailure
    def testCustomAssert(self):
        self.assertHappyFace(':(') # must fail.

But prints a warning-message during testing. I use it with Django and Nose. Which others have seen, too.

/usr/lib64/python3.4/unittest/case.py:525: RuntimeWarning: TestResult has no addExpectedFailure method, reporting as passes RuntimeWarning)

I came here to find a better solution, but found none. So I at least wanted to tell others that come what I've been working with.

Comments

7

In my previous project i had to do something like test driven development, so i have written some test case which must catch certain types of error. If it don't gets the error then i have messed up something. Here i share my code.

from django.test import TestCase
from django.contrib.auth.models import User

class ModelTest(TestCase):

def test_create_user_with_email(self):

    with self.assertRaises(TypeError):
        email = "[email protected]"
        password = 'testpass1'

        user = User.objects.create_user(
            email = email,
            password = password,)

        self.assertEqual(user.email, email)
        self.assertTrue(user.check_password(password))

You can see i have tried to create a user with email and password but default Django user model need "username" and "password" arguments to create user. So here my testcase must raise "TypeError". And that what i tried to do here.

Comments

1

In case anyone coming from search is looking for a method similar to @Chris, but doesn't like the x unittest displays when the test "fails successfully", I stumbled across this:

from django.test import TestCase

class ExampleTests(TestCase):
    def test_fails(self): # Passes
        with self.assertRaises(self.failureException):
            self.assertTrue(False)

I needed it for a sanity check to make sure a custom assertion actually failed when it's supposed to fail.

Comments

-6

Something like this should work:

thing = Thing.objects.create(name='1234')  
# not sure if you need here .get() or if. create() truncates the field on its own
self.assertEqual(thing.name, '123') # or: self.assertEqual(len(thing.name), 3)

-- but such test looks weird :-)

Also, note that MySQLdb backend will raise Warning exception to notify you of truncating the string, so you might want to check it with assertRaises.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.