The Problem
I use Git on a daily basis, as my default version control system and integration with Github. I also am acquainted with writing tests, and how to integrate those with Git – e.g., which test files should be gitignore
’d vs tracked. However, I just encountered a new wrinkle, which is how to deal with tests that rely on Git itself, or rather, the absence of Git. This pretty much only comes up when building tools for the developer toolchain / devops, but that is exactly what I’m doing at the moment.
I’ve actually previously created a Git based tool and test suites for it; however, those tests used the easy trick of nested git repositories. If you have project
which is a git project, and you create project/git-test
and then run git init
inside git-test
, you end up with a separate git repository inside your actual source code’s one, complete with separate tracking (there are issues however, if you forget to gitignore the child repository).
The complication is when you need to test your tool against a directory that is not part of a git-initialized repository. For example, if your test makes sure that your code throws the appropriate error when ran against a non-git project, where git log
will fail with fatal: not a git repository
, you need such a directory to exist to test against. Where do you create that test directory?
- If you create it inside your source code, like
src/__tests__/tmp
, it will inherit the git tracking ofsrc
. - If you try to go up a level from your source code (e.g.
src/../tmp
) and create it there, you can’t be sure that won’t lead to a directory collision, or that your code hasn’t ended up nested in another git repository. - Creating it at the highest level (e.g.
/
) seems like a bad idea, and one might argue it doesn’t respect the User’s filesystem
The Solution: OS Designated Temporary Paths
I found the solution to this issue in two places, at roughly the same time. The first place was in checking out how an existing tool, gitchangelog
, creates temporary directories with Python, and the second place was in the NodeJS fs
docs, when I was trying to look up some methods.
If you are more seasoned with {{devops, IT sysadmin, etc.}} than I am, you might have already identified the solution long ago; OS temporary directories.
Essentially, over time a standard practice has emerged for operating systems to expose a temporary directory, which is periodically cleaned, and guaranteed to exist on boot.
Standard Library Wrappers and Implementations
Using temporary directories is such a common practice that many “standard libraries” have methods built-in to get the OS temp dir path, as well as create temporary folders directly.
Here are some examples:
- NodeJS
os.tmpdir()
to get system temp dir (docs, implementation)- Also see sindresorhus/temp-dir, for version that always returns real path, even if system returns symlink
fs.mkdtemp()
to make temporary directory (docs)
- Python –
tempfile
(docs)tempfile.gettempdir()
to get system temp dir (docs, implementation)- Wrapper around
_get_default_tempdir()
to get system temp dir (private, implementation) - OS default strings are from
_candidate_tempdir_list()
(implementation)
- Wrapper around
tempfile.mkdtemp()
to make temporary directory (docs, implementation)
- QT5
Note that using temporary directories goes beyond just finding the right directory; the sub-directories that you create also need to have unique names that won’t collide – many of the methods above automatically do this for you.
Usage Considerations
Using temporary directories comes with some important caveats, the most important being that your data is unlikely to be persisted for very long. Some OSes will periodically clear temporary storage, or do so on events like reboots, or users might even choose to manually clear their own temporary folder when their HDD is nearing capacity.
For my use-case, creating a non-git-initialized directory with a few files for testing, this is perfect. In fact, best practice would be to clear / remove this test directory after each run, so the fact that it might not be persisted past a reboot is a moot concern anyways.