In a Django test, how do I save the database object and then retrieve it from the database?

I am using Django 1.8. I wrote the following code to check that the host is pre_save

working correctly, but this code seems very inelegant. Is this the "correct way" to write this type of unit test?

class PreSaveTests(TestCase):
    def test_pre_save_hook(self):
        person = Person(name="Joe")
        person.save()
        person2 = Person.objects.get(pk = person.pk)
        # Confirm that the pre_save hook ran.
        # The hook sets person.is_cool to True.
        self.assertEqual(person2.is_cool, True)

      

This works great, but it seems ugly.

The really ugly part is that person

and person2

are the same database object. The only difference is what person2

was pulled from the database.

+3


source to share


4 answers


First, I'm not sure why you think this is ugly: it seems like a perfectly reasonable way to test this functionality.



However, you could have made it easier. Although Django instances have no ID, i.e. Two instances, checked out from the database separately, will not change until they are saved and checked out - when the pre-save cycle runs, it will modify the existing instance. So it actually person

gets the mod to install is_cool

, so there is no need to fetch and check person2

.

+4


source


What you are doing in your test is great. However, in my opinion you can simplify / improve it a bit.

I think you should be using factories (you can use FactoryBoy

). This way you don't have to update your test when you add / remove required fields in your model. In addition, you can remove unnecessary information from your test. In this case, the fact that the person's name is Joe

completely irrelevant.

You can replace:

person = Person(name="Joe")
person.save()

      

from:



person = PersonFactory.create()

      

As Daniel mentioned, you don't need to reload the Person instance. So you don't need to do this:

person2 = Person.objects.get(pk = person.pk)

      

Finally, a little tip, you can use assertTrue

instead assertEquals(something, True)

:

class PreSaveTests(TestCase):

    def test_pre_save_hook(self):
        person = PersonFactory.create()
        self.assertTrue(person.is_cool)

      

+4


source


I think there is a good way to test simple functionality. However, "by the book" a unit test is best defined by mocking the functionality of the database. This way you can unit test your methods without worrying about what the database is doing.

I usually do this with a mock library (included in 3.x). Without going into too much detail, as described in other answers, you can use a patch to mock the model under test (Person) and then force it to return something.

Take a look also at mock-django, it provides a lot of functionality related to this, https://pypi.python.org/pypi/mock-django and here https://docs.python.org/3/library/unittest. mock.html

I cannot verify this (and I will make it more explicit as usual) for Python 3. Within the unittest class, you can create a test like this.

# first patch your class
@patch('my_app_name.models.Person')
def test_my_person(self, person_mock)
    person_mock.objects = MagicMock()
    person_mock.objects.configure_mock(get.return_value='guy_number_1')
    # then you can test your method. For example if your method change the guy name.
    self.assertEquals(my_method('guy_number_1'), 'GUY_NUMBER_1')

      

The code is not the best, but the idea is that you are mocking the database, so if the database connection slows down, your unittest will not work (as it should be because you are not testing Django functionality and database connection) ...

This was helpful for me when building and testing automatically without deploying a test database. Then you can add integration tests to cover the functionality of your database.

I will continue the explanation if it is not clear enough.

The useful things that are sometimes ignored in the mock are the configure method, side_effect for mocking exceptions, and sometimes you need to reload the module to apply the fixes.

+1


source


You can directly check the property in the request without actually getting the object:

class PreSaveTests(TestCase):
    def test_pre_save_hook(self):
        person = Person(name="Joe")
        person.save()
        # Confirm that the pre_save hook ran.
        # The hook sets person.is_cool to True.
        self.assertTrue(
            Person.objects.filter(pk = person.pk, is_cool=True).exists()
        )

      

0


source







All Articles