How delete image files after unit test finished?
Asked Answered
B

4

7

In unit test I create 3 objects (articles) at the beginning. After test complete I notice that there are 3 images in media_root folder.

Question: How to delete that images which was created after test finished?

P.S. I tried to use next code but it delete media_root folder.

def tearDown(self):
        rmtree(settings.MEDIA_ROOT, ignore_errors=True)

NOTE: Problems in methods test_article_form_valid and test_article_crud.

tests.py:

class ArticleTestCase(TestCase):
    def setUp(self):  
        self.image = open(os.path.join(BASE_DIR, 'static/images/tests/image.jpg'), "r")

    def test_article_form_valid(self):
        data = {
            'head': 'TEXT',
        }
        files_data = {
            'image': SimpleUploadedFile(
                name=self.image.name,
                content=self.image.read(),
                content_type='image/jpeg'
            )
        }
        form = ArticleForm(data=data, files=files_data)
        self.assertTrue(form.is_valid())  <-- ERROR

    def test_article_crud(self):
        response = self.client.get(reverse("article:article_create"))
        self.assertEquals(response.status_code, 200)
        response = self.client.post(
            reverse("article:article_create"),
            data={
                'head': 'TEST',
                'image': self.image
            },
            follow=True,
            format='multipart'
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(Article.objects.all().count(), 1) <-- ERROR

    def test_article_view(self):
        first_article = Article.objects.create(
            pk=150,
            head='First',
            image=SimpleUploadedFile(
                name=self.image.name,
                content=self.image.read(),
                content_type='image/jpeg'
            )
        )

        second_article = Article.objects.create(
            pk=160,
            head='Second',
            image=SimpleUploadedFile(
                name=self.image.name,
                content=self.image.read(),
                content_type='image/jpeg'
            )
        )

        third_article = Article.objects.create(
            pk=170,
            head='Third',
            image=SimpleUploadedFile(
                name=self.image.name,
                content=self.image.read(),
                content_type='image/jpeg'
            )
        )
        [***]

ERROR:

FAIL: test_article_crud (article.tests.ArticleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/nurzhan/CA/article/tests.py", line 55, in test_article_crud
    self.assertEqual(Article.objects.all().count(), 1)
AssertionError: 0 != 1

======================================================================
FAIL: test_article_form_valid (article.tests.ArticleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/nurzhan/CA/article/tests.py", line 117, in test_article_form_valid
    self.assertTrue(form.is_valid())
AssertionError: False is not true
Blastema answered 18/9, 2017 at 7:4 Comment(0)
P
10

I found this article and it worked to me

import shutil, tempfile
from django.test import TestCase, override_settings

MEDIA_ROOT = tempfile.mkdtemp()

@override_settings(MEDIA_ROOT=MEDIA_ROOT)
class MeuPetTest(TestCase):
    @classmethod
    def tearDownClass(cls): 
        shutil.rmtree(MEDIA_ROOT, ignore_errors=True)
        super().tearDownClass()
Principal answered 13/3, 2019 at 23:8 Comment(1)
It doesn't work correctly, it creates the files in the new MEDIA_ROOT, yes, but also in the MEDIA_ROOT of the project, at least for me.Operand
H
2

I believe the best solution is to create a directory that will contain a '/media' folder, this directory can then be deleted in the tearDown function.

Based on this article, I tried the solution and it worked perfectly. You won't even notice the creation and deletion of the test directory and the files created in your tests won't be in your default media location.

from django.test import override_settings
import shutil

TEST_DIR = 'test_data'

class SomeTests(TestCase):
    ...

    # use the `override_settings` decorator on 
    # those methods where you're uploading images

    @override_settings(MEDIA_ROOT=(TEST_DIR + '/media'))
    def test_file_upload(self):
        ...
        # your code
        ...

...


# then create a tearDownModule function
# to remove the `TEST_DIR` folder at the 
# end of tests

def tearDownModule():
    print "\nDeleting temporary files...\n"
    try:
        shutil.rmtree(TEST_DIR)
    except OSError:
        pass

If you want to implement this solution in DRF, your code should look like this:

from django.test import override_settings
import shutil
from rest_framework.test import APITestCase

TEST_DIR = 'test_data'

class SomeTest(APITestCase):
    ...

    # use the `override_settings` decorator on 
    # those methods where you're uploading images
    
    # do this if any logic exists to create files
    # in your setUp
    @override_settings(MEDIA_ROOT=(TEST_DIR + '/media'))
    def setUp(self):
        ...
        # your code
        ...

    @override_settings(MEDIA_ROOT=(TEST_DIR + '/media'))
    def test_file_upload(self):
        ...
        # your code
        ...

    def tearDown(self):
        print "\nDeleting temporary files...\n"
        try:
            shutil.rmtree(TEST_DIR)
        except OSError:
            pass
Herlindaherm answered 3/4, 2022 at 2:52 Comment(0)
H
0

Use tempfile module in python, use it as settings.MEDIA_ROOT in the TestCase setUp() method,

from django.conf import settings
import tempfile

def setUp(self):
    settings.MEDIA_ROOT = tempfile.mkdtemp()

Then, the files created in the test will be automatically removed when the test is finished.

UPDATE:

Then, the files created in the test will not be automatically removed when the test is finished, so do not forget to delete the temporary directory after finishing the test.

Honeysuckle answered 18/9, 2017 at 7:8 Comment(9)
But the OP wants to retain the directory, just remove the files from within it, I think.Paradiddle
@Paradiddle absolutely right! I need delete only image files which was created in my test. Dont touch to folder. Do you have any other ideas?Blastema
The point of testing is to mock a certain functionality without affecting the actual database and testing, isn't it? If you use tempfile module you don't have to create actual image objects, but create a mock for them and do your testing then after the test, the objects will automatically deleted. There doesn't even arise a question about removing the files, there are no files created, on run time they are created and destroyed.Honeysuckle
@Honeysuckle your code raise error in other my test. I update my post with error message and some code. Can you check it pls again. Do you have ideas why I have such errors after your code?Blastema
You did not add my code in the setUp() method of your TestCase, add settings.MEDIA_ROOT = tempfile.mkdtemp() in your setUp() method. Then give it a try.Honeysuckle
Also, the content_type argument in SimpleUploadedFile should be content_type='multipart/form-data', because, you are testing a form input.Honeysuckle
I test your code but unfortunately it didn't help me =(Blastema
"the files created in the test will be automatically removed when the test is finished" this is not true. The docs state that The user of mkdtemp() is responsible for deleting the temporary directory and its contents when done with it.Bolding
Sorry man. Totally misunderstood. Thank you for letting me realise the mistake. Appreciate the support and help.Honeysuckle
S
0

For me best solution was to write a function which delete images and add it into test class tearDown method.

def delete_test_image():
    images_path = os.path.join(PROJECT_PATH, 'media/images')
    files = [i for i in os.listdir(images_path) 
             if os.path.isfile(os.path.join(images_path, i))
             and i.startswith('test_image_')]

    for file in files:
        os.remove(os.path.join(images_path, file))
Starr answered 18/7, 2021 at 20:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.