13

I have a django app and I am running some unit tests on it. So the problem I am having is not when one test inserts into the test db. It is the tests that come after. Since each test is not saving the transaction, the entry from a previous test is not there which is fine, although the auto increment id's are increasing as if there are still entries into the database. Which I need to fix because I am inserting more data where I cannot control the id's given to it and need to be able to grab this specific data for the test. If I hard code the code to grab the objects I will have to change the code for every time I add a new test, which is not ideal.

I have multiple tests running, but for simplicity sake, I will show two.

from django.test import TestCase
from app.models import Model

class VersionMerge(TestCase):
   fixtures = ['initial_test_data.json']

   def test_model_test1(self):
       *Insert new data*
       *grab new data in*
       *Check data*

   def test_model_test2(self):
       *Insert new data*
       *grab new data*
       *Check data*

The problem arises in test_model_test2 where when I try to grab the new data, I have to print the object out to see the id's to be able to grab it.

I have a solution on how I can fix this on the actual database but not the test one. For mine I need to be able to connect to a docker container and run a psql command to reset the table_id_seq.

docker exec -t  $CONTAINER_ID psql --dbname=test_database_name -username=user -c "SELECT setval('modelName_appName_id_seq', 2, true)"

This will go to the table and set the last id value used to be 2 to make the next id 3. However whenever I try to run the command from inside python using

cmd = "command above"
os.system(cmd)

and when I run this I get the following error.

sh: 1: docker: not found
sh: 1: docker: not found

Looking for any help on this, either a new solution to the problem or improvements on mine.

TLDR; I need to be able to modify data in the database that the django unit tests create.

Ryan w
  • 528
  • 1
  • 7
  • 21

3 Answers3

6

If you need a test to reset the primary key sequence, you can issue a RawSQL query doing that. How to do that exactly is answered in this StackOverflow question.

An easier option is available if you're using pytest. We're using pytest and pytest-django in all our Django projects and it makes testing a breeze. pytest-django provdes a database fixture that can take a boolean parameter to reset the sequences. Use it like so:

@pytest.mark.django_db(transaction=True, reset_sequences=True)
def mytest():
    [...]
emyller
  • 2,648
  • 1
  • 24
  • 16
jnns
  • 5,148
  • 4
  • 47
  • 74
  • Great find with py.test. It took me a while to figure out why my tests were failing when moving from sqlite to posgres, adding reset_sequences did the trick because in posgres the IDs were incrementing between test functions. – Aaron Oct 14 '20 at 14:56
4

I got this to work by replacing TestCase with TransactionTestCase and set reset_sequences=True. However the tests are running slower.

from django.test import TransactionTestCase

class ViewTest(TransactionTestCase):
    reset_sequences = True

    def test_view_redirects(self):
       ...

Here's the doc

Pooya Kamranjam
  • 355
  • 3
  • 9
1

You are using a fixtures file - put whatever data you want in there; then edit it in your test as needed.

Although, that is harder to maintain. In my opinion - there are far better options that may work much more like what you intend.

You're better off using something like factory_boy and generating the models (and related foreign keys) at instantiation with dummy data you provide.

That way you know exactly what is being tested and it's completely independent of everything else. The nice part is that with factory_boy you will have a factories.py file you can keep up to date much easier than working with some fixture.

There are other options like Mixer or model_mommy, although I only have experience with factory_boy and mixer.

With factory_boy it might look something like this:

   def test_model_test1(self):
       factory.ModelFactory(
       some_specific_attribute='some_specific_value'
       )
       model = Model.objects.all().first()
       # Test against your model
Hanny
  • 580
  • 3
  • 16
  • 44