"i have done a couple bad things"


number of years i have lived on this earth
over 30

what is your opinion on colocated unit tests? i will put my thoughts under the fold.


the sveltekit starter colocates tests with source files by default (or maybe it's a vitest default?), and it stands out as a Choice. i like it? colocation makes unit tests more obvious and present, and i think that decreases the risk of them becoming ~shitty and useless~ outdated when the source files change.

the downside is... clutter? i don't feel terribly concerned about that. it's also way less a pain in the butt than maintaining a parallel file hierarchy, if we consider the other scenario of having an entirely separate directory called test with files that correspond to every source file...


You must log in to comment.

in reply to @thricedotted's post:

this actually sounds great and I wish it was an option with our testing stack

if you're using VS Code it might be worth using #region around your tests to cut down on the clutter

we like them in their own files. no strong opinion on same vs. nearby directory.

choosing where to put module boundaries is.... well, we think of source code as primarily didactic in function, the highest concern when writing the program is making sure someone can understand it. otherwise it's useless and will be consigned to the dustbin of history and all that. so we kind of pick one particular core concept for each source file and then we put as much as we can into that file to explicate the concept properly, because making it too big it's impossible for people to fit in their brains, but slicing things up too narrow is also a failure at communication.

putting tests in the same file cuts into that, it takes up space in the reader's brain so we can't put as many distinct ideas in the file. so we prefer them to be elsewhere.

hope that makes sense! great discussion prompt, thanks so much for it

oh, i hadn't even considered colocation in same file -- i was thinking directory! completely agree on placing an intentional boundary between source code and tests at the file level. and just practically, in use i want to see them side-by-side, not interspersed with one another.

i'm glad people have opinions on it! i find it interesting

yeah, and we've been using a lot of Rust lately which is what prompted our thoughts.

agreed that it's not super compatible with web technologies, though if someone really cared it would certainly be possible to design a template format that works with it.

that, or… a bundler plugin!

this is perhaps doable with Vite, and/or an extension of the Vue Single File Template format (which was always a great part of Vue)

((yes, JSX is fine and all that, but… it feels like that style doesn't lend itself to design))

I like putting unit tests as close to the thing they're testing as possible, so when I'm writing Rust I'll usually put a test module in the file I'm testing for example. It makes changing the module (and therefore the test) less onerous later, and helps the test serve as documentation for the code it's testing. (I also like the ability to put tests in the docs via rustdoc).

big fan of having tests in the same directory as the implementation. it's very useful to look in one place to find everything concerned with a particular bit of source -- I mostly work with react components and love having the implementation in the same dir as relevant tests, styles, storybook, etc

definitely a fan (although ive only done it once or twice). having tests in the same module as the code is noisy, but:

  • in general i prefer "structure of pairs" over "pair of structures" for organizing things; it means that the whole unit (the pair) is in one place
  • it means tests' types stay up-to-date by default
  • it makes tests easier to find and read for new programmers (although nobody's reading my code anyway)
  • this is the big one for me: it encourages writing tests. no need to switch back and forth between files a million times; no need to remember "oh yeah i have a tests/ directory", etc, etc. makes testing a part of the natural flow of programming. especially so if your tests are part of your hot reload, but i'm meh on that

I do web/Node dev and the "separate tests folder" seems to have fallen completely out of favour for the (much better) "separate test file in the folder with the code file". I think I prefer this over having both in one file (though I haven't tried that) mostly because in my experience a code file might be 150 lines and the tests can still run to 400 — I navigate files by the scrollbar minimap, so I don't want ¾ of the file to be something else. That said I generally will break a big file into smaller files any chance I get and I know that's not universal.

Ultimately I think the best solution will depend on the people and the tools you've got more than any particular philosophy, (but ofc there are still lots of objectively bad solutions).

I greatly prefer to have separate directories for tests. It might be annoying to mirror filename changes across both directories, but this also makes it easy and natural to create test files that cover multiple aspects of the project at once or aren’t directly tied to a specific namespace.

I use a vim plugin called “projectionist” that allows me to define the mirror file path for a source and a test file, so I can jump from src/foo/bar/baz.clj to test/foo/bar/baz.clj (creating as necessary) and back with :A. That removes all friction from test generation or updating.

I work in Clojure. Because Clojure is hosted on Java, it mirrors a lot of Java’s idioms for test file placement, which is “separate directory, mostly mirroring source file structure.”

Clojure has a comment macro that evaluates to nil, so you can put repl-based tests in there (forms to eval and check results) but that’s not automated. There are a couple libraries which can convent those to automated tests, but I don’t use them. I prefer to leave comment forms as loose documentation and use real tests.

Hopefully cohost doesn't lose this comment this time. (Don't write cohost comments while on wifi that's likely to drop)

So I've been thinking about this and there are many gradations possible for the degree to which tests are colocated with the code they're testing.

At one extreme, you have things like pythons doctest where tests are written in specially formatted documentation comments inside the function that they're testing.

From there you can go slightly further out to "tests are in the same file, and not separated into a separate module/class in that file from what they're testing" and then to "tests are in the same file, but in a separate module/class from what they're testing". (that second one several people below mention doing in rust)

Then there's "separate file, but the file convention is such that main code and test code is shown together by most tools" (there's a strong convention in go code to do this)

Then "separate file, but the convention groups all the test files together in most tools" (I've seen an occasional python project do this, though separate directory is more common).

Then there's "separate directory, but the directory structure mirrors that of the non-test code". (This is generally the way java projects are laid out)

Then "separate directory, and the file/directory structure is something test-specific totally separate from the way the non-test code is laid out". (Think CPAN modules and the t/ directory; I'm also used to Haskell projects using a convention like this)

Then "separate source control repository", which may strike you as horrible but is may be appropriate when you need some sort of conformance suite to test potentially multiple implementations of some standard.

I suppose one could count not writing the tests at all as the ultimate rejection of colocation, in that tests then do not exist in the same universe as the main code, but that seems silly.

One gradation that it occurs to me could exist, but I don't think I've ever seen: tests that exist as separate functions but within the same organizing structure that's smaller than a file. For example, this might be a class that would be a real class but would also have some tests just written into it next to non-test methods, or a file with multiple modules and within the same module there'd be both regular functions and tests next to each other.

There's probably a good reason for that, but "there's a good reason to avoid doing this ridiculous thing with software" has rarely stopped people before, so I'm not sure why I've never seen that organization.

The closest I can think of is classes that have functions that verify or assert things about internal state that are only called from within tests.