Django prueba FileField usando accesorios de prueba


Estoy tratando de construir pruebas para algunos modelos que tienen un campo de archivos. El modelo se ve así:

class SolutionFile(models.Model):
    '''
    A file from a solution.
    '''
    solution = models.ForeignKey(Solution)
    file = models.FileField(upload_to=make_solution_file_path)

He encontrado dos problemas:

  1. Al guardar datos en un dispositivo usando ./manage.py dumpdata, el contenido del archivo no se guarda, solo el nombre del archivo se guarda en el dispositivo. Si bien encuentro que este es el comportamiento esperado, ya que el contenido del archivo no se guarda en la base de datos, me gustaría incluir de alguna manera esta información en el accesorio para las pruebas.

  2. Tengo un caso de prueba para subir un archivo que tenga este aspecto:

    def test_post_solution_file(self):
        import tempfile
        import os
        filename = tempfile.mkstemp()[1]
        f = open(filename, 'w')
        f.write('These are the file contents')
        f.close()
        f = open(filename, 'r')
        post_data = {'file': f}
        response = self.client.post(self.solution.get_absolute_url()+'add_solution_file/', post_data,
                                    follow=True)
        f.close()
        os.remove(filename)
        self.assertTemplateUsed(response, 'tests/solution_detail.html')
        self.assertContains(response, os.path.basename(filename))
    

Mientras que esta prueba funciona bien, deja el archivo cargado en el directorio media después de terminar. Por supuesto, la eliminación podría ser atendida en tearDown(), pero me preguntaba si Django tenía otra forma de lidiar con esto.

Una solución en la que estaba pensando era usar una carpeta de medios diferente para las pruebas que debe mantenerse sincronizada con los accesorios de prueba. ¿Hay alguna manera de especificar otro medio directorio en settings.py cuando se están ejecutando pruebas? ¿Y puedo incluir algún tipo de gancho a dumpdata para que sincronice los archivos en las carpetas multimedia?

Entonces, ¿hay una forma más pitónica o específica de Django de lidiar con las pruebas unitarias que involucran archivos?

Author: Gabriel Hurley, 2010-02-15

4 answers

Django proporciona una gran manera de escribir pruebas en campos de archivos sin perder el tiempo en el sistema de archivos real - use un SimpleUploadedFile.

from django.core.files.uploadedfile import SimpleUploadedFile

my_model.file_field = SimpleUploadedFile('best_file_eva.txt', 'these are the file contents!')

Es una de las características mágicas de django-que-no-aparecen-en-los-documentos:). Sin embargo se hace referencia a aquí.

 19
Author: waterproof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-12-11 01:10:01

Puede anular la configuración MEDIA_ROOT para sus pruebas utilizando el decorador @override_settings() como está documentado :

from django.test import override_settings


@override_settings(MEDIA_ROOT='/tmp/django_test')
def test_post_solution_file(self):
  # your code here
 5
Author: w00kie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-09 22:27:45

He escrito pruebas unitarias para toda una aplicación gallery antes, y lo que funcionó bien para mí fue usar los módulos tempfile y shutil de python para crear copias de los archivos de prueba en directorios temporales y luego eliminarlos todos.

El siguiente ejemplo no está funcionando / completo, pero debería ponerte en el camino correcto:

import os, shutil, tempfile

PATH_TEMP = tempfile.mkdtemp(dir=os.path.join(MY_PATH, 'temp'))

def make_objects():
    filenames = os.listdir(TEST_FILES_DIR)

    if not os.access(PATH_TEMP, os.F_OK):
        os.makedirs(PATH_TEMP)

    for filename in filenames:
        name, extension = os.path.splitext(filename)
        new = os.path.join(PATH_TEMP, filename)
        shutil.copyfile(os.path.join(TEST_FILES_DIR, filename), new)

        #Do something with the files/FileField here

def remove_objects():
    shutil.rmtree(PATH_TEMP)

Corro esos métodos en los métodos setUp() y tearDown() de mis pruebas unitarias y funciona muy bien! Tienes una copia limpia de tus archivos para probar tu filefield que son reutilizables y predecibles.

 3
Author: Gabriel Hurley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-15 22:14:23

Esto es lo que hice para mi prueba. Después de cargar el archivo debe terminar en la propiedad photo del objeto modelo de mi organización:

    import tempfile
    filename = tempfile.mkstemp()[1]
    f = open(filename, 'w')
    f.write('These are the file contents')
    f.close()
    f = open(filename, 'r')
    post_data = {'file': f}
    response = self.client.post("/org/%d/photo" % new_org_data["id"], post_data)
    f.close()
    self.assertEqual(response.status_code, 200)

    ## Check the file
    ## org is where the file should end up
    org = models.Organization.objects.get(pk=new_org_data["id"])
    self.assertEqual("These are the file contents", org.photo.file.read())

    ## Remove the file
    import os
    os.remove(org.photo.path)
 0
Author: Gustavo Ambrozio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-04 20:12:29