47

My Python project imports pytest 2.9.0 fine without any issues.

I want to create a new empty directory that will last only the life of the test session. I see that pytest offers temporary directory support:

https://pytest.org/latest/tmpdir.html

You can use the tmpdir fixture which will provide a temporary directory unique to the test invocation, created in the base temporary directory.

tmpdir is a py.path.local object which offers os.path methods and more. Here is an example test usage:

The source-code for pytest shows that def tmpdir is a global/module function: https://pytest.org/latest/_modules/_pytest/tmpdir.html

However my test file fails:

import pytest

# ...

def test_foo():
    p = pytest.tmpdir()

With error:

AttributeError: 'module' object has no attribute 'tmpdir'

Doing from pytest import tmpdir fails with:

ImportError: cannot import name tmpdir

Dai
  • 141,631
  • 28
  • 261
  • 374
  • 4
    The documentation also lists some examples. Does `def test_foo(tmpdir):` work for you? – vaultah Mar 17 '16 at 19:32
  • 1
    The [documentation](http://pytest.org/latest/tmpdir.html) illustrates how to use it, and [this](http://stackoverflow.com/a/20545394/1832539) SO post also has an example. Does that work for you? – idjaw Mar 17 '16 at 19:36

2 Answers2

75

UPDATE: Use tmp_path instead of tmpdir. tmp_path is a pathlib.Path/pathlib2.Path. tmpdir is a py.path (Actually LocalPath), which has offered syntax very similar to pathlib.Path. See pytest issue.

Usage of py.path is no longer recommended by the developers.

Syntax is similar, eg:

def test_something_else(tmp_path):
    #create a file "myfile" in "mydir" in temp directory
    f1 = tmp_path / "mydir/myfile"
    f1.parent.mkdir() #create a directory "mydir" in temp folder (which is the parent directory of "myfile"
    f1.touch() #create a file "myfile" in "mydir"


    #write to file as normal 
    f1.write_text("text to myfile")

    assert f1.read_text() == "text to myfile" 

ORIGINAL: I looked into it an also found the behaviour peculiar, and I summarize what I learned below, for others who don't find it so intuitive.

tmpdir is a predefined fixture in pytest similar to how setup is defined here:

import pytest

class TestSetup:
    def __init__(self):
        self.x = 4

@pytest.fixture()
def setup():
    return TestSetup()

def test_something(setup)
    assert setup.x == 4

Thus tmpdir is a fixed name defined in pytest which is passed on to your testfunction if you have it as an argument name.

Example usage:

def test_something_else(tmpdir):
    #create a file handle for "myfile" in "mydir" in temp folder
    f1 = tmpdir.mkdir("mydir").join("myfile")

    #create a file handle for "myfile" in temp folder
    f2 = tmpdir.join("myfile")

    #write to file as normal (this is what actually creates the file)
    f1.write("text to myfile")

    assert f1.read() == "text to myfile"

This works when you run it using pytest, eg running py.test test_foo.py in the terminal. The file generated in this way has read and write access, and can be viewed later in your systems temporary folder (for me this was /tmp/pytest-of-myfolder/pytest-1/test_create_file0)

gibson
  • 543
  • 1
  • 5
  • 15
M.T
  • 4,917
  • 4
  • 33
  • 52
  • 3
    +1 for pointing out `tmpdir` / `tmp_path` difference. I was getting some weird errors because I thought `tmpdir` was already a pathlib.Path – jrieke Sep 27 '20 at 00:55
  • `tmp_path.mkdir("mydir")` does not work for me. I am getting `TypeError: an integer is required (got type str)`. I had to create a directory object first, and then `mkdir()` it. or use `os.mkdir(tmp_path / "mydir")`. (linux -- Python 3.6.8, pytest-6.2.2) – Kuchara Feb 22 '21 at 17:21
  • @Kuchara Well spotted. You are of course right. I have edited the answer. – M.T Mar 04 '21 at 08:53
  • 1
    what is tmp_path supposed to equal to in your update code @M.T? Should we replace it with a string of the directory in which we want to create temp files? – lightbox142 Sep 18 '21 at 03:03
  • @lightbox142 the value of `tmp_path` will be a randomly generated temporary directory of type `pathlib.Path`. You do not have to replace it, pytest will detect the argument name (as pytest has a fixture with that specific name, the same way the `setup`argument is passed in the example above) and fill in the argument before the test is run. – M.T Sep 18 '21 at 16:34
  • shouldn't it be `f1.open().read()` instead of `f1.read()`? – Jonas Nov 23 '22 at 12:27
  • 1
    @Jonas You are right, well spotted. I will correct it – M.T Nov 23 '22 at 14:08
14

You just have to pass the tmpdir as a function parameter as it is a py.test fixture.

def test_foo(tmpdir):
    # do things with tmpdir
Ilyas
  • 327
  • 1
  • 3
  • 12