In a Go test, what does t.TempDir() return and who deletes that directory?
answer
- scratch space, made for you
- you never write the removal
- each call hands back a new path
- removal waits for subtests too
- creation failure ends the test
basics
~20 st.TempDir() returns the path of a fresh, empty directory created for that test. The testing package removes it automatically once the test and all of its subtests have completed, so the test never writes its own removal code.
solid answer
~50 s`t.TempDir()` creates a new empty directory on disk and returns its path. Every call returns a different directory, and the name is derived from the test's name so failures are easy to trace. You do not delete it: `TempDir` registers the removal with `t.Cleanup`, so the directory is removed once the test **and all of its subtests** have completed — which is why it is safe to hand the path to subtests, including parallel ones. If the directory cannot be created, `TempDir` fails the test immediately by calling `Fatal`, so there is no error to check. If the removal later fails — typically because something still holds an open file inside it — the testing package reports that as a test failure rather than swallowing it. Compared with `os.MkdirTemp` plus a manual `defer os.RemoveAll(dir)`, it is shorter and it cleans up at the right moment.
code
go · 9 lines// Before: three lines of bookkeeping.
dir, err := os.MkdirTemp("", "unpack")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
// After: one line; removed when the test and its subtests complete.
dir := t.TempDir()go deeper
Be ready to say what the call returns, that each call gives a new directory, and that removal is automatic. Knowing you do not write os.RemoveAll yourself is most of the answer.
Explain that the removal is registered through t.Cleanup at call time, not deferred, and that it therefore waits for subtests. Mention that cleanups run last-registered-first.
Show you know the failure mode: removal that fails is reported as a test failure, so a test with all assertions green can still go red because a file was left open.
Frame it as a team default: helpers take *testing.T and allocate their own scratch space so no caller can leak a directory, and no test writes into the repository tree.
## The problem it solves A test for anything that touches disk — a CLI that unpacks an archive into a working directory and rewrites files in place, a config loader, a log rotator — needs somewhere to write. Writing into the package directory pollutes the repository and makes tests interfere with each other; writing into a fixed path like `/tmp/mytest` makes two concurrent runs collide. The classic answer was: ```go dir, err := os.MkdirTemp("", "unpack") if err != nil { t.Fatal(err) } defer os.RemoveAll(dir) ``` Three lines of boilerplate, an error to handle, and a removal that is easy to forget or to place wrongly. `t.TempDir()` collapses all of that into one call. ## What the call does `TempDir` is a method on `*testing.T` (and on `*testing.B` and `*testing.F`). It creates a new directory under the system temporary directory and returns its absolute path as a `string`. There is no error return: if creation fails, `TempDir` terminates the test by calling `Fatal`, on the reasoning that a test which cannot get scratch space has nothing useful left to do. The directory name incorporates the test's name (with characters that are awkward in a path replaced), so when a test leaves something behind or a log line prints the path, you can tell which test owned it. **Each call returns a distinct directory.** Calling `t.TempDir()` twice in one test gives you two separate empty directories, which is exactly what you want when a test needs a "source" tree and a "destination" tree. Both are removed automatically. ## When the removal happens — the part people get wrong `TempDir` does not use `defer`. It registers the removal by calling `t.Cleanup` at the moment you call `TempDir`. That distinction decides the timing: - A `defer` inside the test function runs when **that function's body returns**. - A function registered with `t.Cleanup` runs when **the test and all of its subtests have completed**. For a plain sequential test the two moments look the same, so nothing surprising happens. They come apart as soon as subtests are involved, and they come apart dramatically once a subtest calls `t.Parallel()`: a parallel subtest is paused until the parent's function body has returned, so a `defer os.RemoveAll(dir)` in the parent deletes the tree *before* the subtests ever look at it. Because `t.TempDir()`'s removal is a cleanup rather than a defer, the directory survives until the last subtest is done. That makes it safe to create a fixture tree once in the parent and share the path with every subtest. ## Cleanup ordering Cleanup functions run in last-registered, first-called order (LIFO). Since `TempDir` registers its removal at call time, anything you register **after** calling `t.TempDir()` runs **before** the directory is removed. That is the natural order you want: open a file inside the directory, register its close, and the close happens first, then the removal. ## When removal fails If the automatic `RemoveAll` fails, the testing package reports it as an error against the test (a message of the form `TempDir RemoveAll cleanup: ...`), so a test whose assertions all passed can still end up red. On Unix-like systems an open file handle does not block removal, so this is rare there; on Windows a still-open handle prevents deletion, and Go retries briefly before giving up. The fix is always the same: close what you opened. ## Practical shape ```go func TestUnpack(t *testing.T) { src := t.TempDir() dst := t.TempDir() if err := os.WriteFile(filepath.Join(src, "a.txt"), []byte("hello"), 0o600); err != nil { t.Fatal(err) } // ... run the code under test against src and dst ... } ``` No error handling, no removal, no leftovers. Helper functions that need scratch space should take `t *testing.T` and call `t.TempDir()` themselves rather than accepting a path from the caller — the cleanup then travels with the helper, and no caller can forget it. ## Limits worth knowing The directory belongs to one test. Two tests, or two parallel subtests that each call `t.TempDir()`, get separate directories and cannot tread on each other — that isolation is a large part of why it exists. It is not a place to stash anything you want to inspect after the run: it is gone by the time the run finishes, so anything you want to keep must be copied out or logged during the test.
- If you call t.TempDir() twice inside one test, do you get the same directory back?No. Each call creates and returns a distinct empty directory, which is handy when a test needs a separate source tree and destination tree. Both directories are registered for removal, so both disappear when the test and its subtests finish.
- What happens if the directory cannot be created?TempDir has no error return; it calls Fatal, so the test stops right there. The reasoning is that a test with no scratch space cannot produce a meaningful result, and forcing every caller to write an error check would only add noise.
- Is it safe for two subtests running in parallel to each call t.TempDir()?Yes. Each subtest gets its own directory, removed when that subtest completes, so they cannot interfere. A directory created by the parent is removed only after every subtest has finished, so a shared fixture tree is safe to pass down.
saying these in an interview costs you the question
- Says you must still call os.RemoveAll on it yourself
- Thinks repeated calls in one test return the same directory
- Assumes the directory disappears when the test function body returns
- Believes one temp directory is shared by all tests in the package
- Expects an error return and writes a check for it