5

I am writing a test case for a Django model with a FileField. I'd like to change the upload path to prevent tests from having side effects on the rest of the system.

I have tried passing a callable to upload_to and patching that in tests:

#models.py
upload_path = lambda x, y: 'files'
class Model(models.Model):
    file = models.FileField(upload_to=upload_path)

#tests.py
test_path = mock.Mock()
test_path.return_value = 'files/test'
@mock.patch('models.upload_path', new=test_path)
class ModelTest(object):
    ...

However this doesn't seem to work, and I believe the reason is that upload_path is dereferenced by FileField before any test code gets run, so it's too late to patch things.

How can I have test code change what upload_to is? Failing that, how can a model check if it's being run by a test?

dgirardi
  • 182
  • 7
  • On the documentation it says: "The target is imported when the decorated function is executed, not at decoration time." I believe that when you patch the 'upload_path' your Model.file object was already set before with a different upload_to when all the importing took place . Maybe its easier to patch the Model.file instead of the upload_path – andrefsp Nov 04 '12 at 12:31
  • @andrefsp I got it working by using `Model.add_to_class("file", models.FileField(upload_to='files/test'))` - I don't know if it's the right way to do it but it works - thanks! – dgirardi Nov 04 '12 at 13:46

1 Answers1

4

I think you're almost there, but to get the late evaluation you want, you need to put file_path in as a variable you want to patch, and then use the lambda to delay binding:

#models.py
upload_path = 'files'
class Model(models.Model):
    file = models.FileField(upload_to=lambda x,y: upload_path)

#tests.py
@mock.patch('models.upload_path', 'files/test')
class ModelTest(object):
    ...
hwjp
  • 15,359
  • 7
  • 71
  • 70