Run python unittest in context of specific directory
Asked Answered
C

3

7

In my python application, I open mp3 files with relative paths from where the program was started. To keep it simple, I made a minimal reproduction of the problem I have in my project here.

Basically, I have a structure like this:

src
└─ main.py
test
└─ test_main.py

In main.py I have a simple function that prints and returns the current working directory:

def get_cwd() -> str:
    directory = os.path.basename(os.getcwd())
    print('Current directory =', directory)
    return directory

So if I cd into the src folder and run python main.py I see:

Current directory = src

This is the desired behavior, as in my program the file paths to the mp3 files are relative to src.

The problem arises when I try to write tests. I can't seem to get a test like this to pass, no matter what I pass to --start-directory and --top-level-directory:

def test_get_cwd(self):
    print('testing get_cwd()')
    current_dir = get_cwd()
    self.assertIsNotNone(current_dir)
    self.assertEqual(current_dir, 'src')

The question: How can I run my tests as if they were running in the context of a specific directory if they are saved to a different directory?

Constraints:

  • the tests must import using absolute paths, as in my example: from src.main import get_cwd
Cowfish answered 19/10, 2018 at 22:5 Comment(0)
A
4

There is a os function to change the directory, try adding os.chdir('src') to your test.

import unittest
import os

from src.main import get_cwd


class TestMain(unittest.TestCase):

    def test_get_cwd(self):
        os.chdir('src')
        print('testing get_cwd()')
        current_dir = get_cwd()
        self.assertIsNotNone(current_dir)
        self.assertEqual(current_dir, 'src')
Aphanite answered 19/10, 2018 at 23:0 Comment(1)
Yes! This works and fits my real program's constraints. Thank you @javrd!Cowfish
A
2

An option is to mock the value given by os.path.basename with a return value of "src"

import unittest
from mock import patch

from src.main import get_cwd


class TestMain(unittest.TestCase):

    @patch('os.path.basename')
    def test_get_cwd(self, basename):
        basename.return_value = "src"
        print('testing get_cwd()')
        current_dir = get_cwd()
        self.assertIsNotNone(current_dir)
        self.assertEqual(current_dir, 'src')
Aphanite answered 19/10, 2018 at 22:27 Comment(3)
That would work for this simple example, but in my real project, I need the real cwd to be src so that the program can find the mp3 files with the same file paths as when it runs normally. The example I made was just to minimally reproduce my problem, I'm not actually using os.getcwd() in my real program, I just need it to return the expected directory so I know it can find the mp3 files with the same relative file paths.Cowfish
I think it should work. Maybe you have to mock something more, but if the path to yours mp3 is fixed depending on your workspace, I don't see why not mocking these path in your controlled test case.Aphanite
It won't work for my real program. I have code that loads up mp3s using relative file paths like audio/example.mp3 where the full path is src/audio/example.mp3. Mocking what cwd or basename returns will not help this problem.Cowfish
E
1

I often do this to create a temporary directory for each test, particularly when testing a file parsing script.

import unittest
import tempfile
import os

class TempDirTest(unittest.TestCase):
    def setUp(self):
        # Create temp folder (deleted when test is done)
        self.tempdir = tempfile.TemporaryDirectory()
        self.addCleanup(self.tempdir.cleanup)

        # Remember original CWD, and go back there after each test
        self._origin_cwd = os.getcwd()
        os.chdir(self.tempdir.name)
        def _revert_cwd():
            os.chdir(self._origin_cwd)
        self.addCleanup(_revert_cwd)

    def test_stuff(self):
        # Create all the relative files & folders you want
        os.mkdir('./nested')
        with open('nested/foo.txt', 'w') as fh:
            fh.write('bar')

This will create a new folder per test (with cleanup), so the files created in one won't influence the next.

Note: If doing lots of tests, or very frequently (eg: Continuous Integration), consider pointing tempfile to a ramdisk, this will increase speed, and lifetime of the disk.

Also consider making your code interface with StreamIO objects instead of filename strs, but this doesn't suit all situations.

Estremadura answered 7/3 at 1:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.