Where to put your tests in a Node project structure

If you've worked on a few different Node applications you've probably seen a few different places you can put your tests. Or if you're new to writing tests you may be wondering what folder/directory to put them in.

For example, do you put the test files alongside the JavaScript files? Or do you put them in their own test folder? In this post I'm going to go over different approaches for organizing these test files so that you can make an informed decision.

Unit tests

Unit tests and integration/end-to-end tests (I know integ and E2E tests are technically different I'm but using them interchangeably here to mean "not unit tests") have different considerations for location and structure. So starting with unit tests first...

Test files in a replicated folder structure

One option for organizing your test files is to mimic the code directory structure and put the tests in the corresponding "layer" folder. So, for example, if you have the following project structure:

  • controllers
    • admin.controller.js
    • blogpost.controller.js
    • ...etc
  • db
    • admin.db.js
    • blogpost.db.js
    • ...etc
  • services
    • admin.service.js
    • blogpost.service.js
    • ...etc
  • utils
    • utils.js

You would then have the following test directory structure (within a top-level tests/ directory):

  • tests
    • controllers
      • admin.controller.test.js
      • blogpost.controller.test.js
      • ...etc
    • db
      • admin.db.test.js
      • blogpost.db.test.js
      • ...etc
    • services
      • admin.service.test.js
      • blogpost.service.test.js
      • ...etc
    • utils
      • utils.test.js

Test files located next to code files

Another option is to put the unit test files right next to the JavaScript file with the code it's meant to test. Following the project structure above, this would look like:

  • controllers
    • admin.controller.js
    • admin.controller.test.js
    • blogpost.controller.js
    • blogpost.controller.test.js
    • ...etc
  • db
    • admin.db.js
    • admin.db.test.js
    • blogpost.db.js
    • blogpost.db.test.js
    • ...etc
  • services
    • admin.service.js
    • admin.service.test.js
    • blogpost.service.js
    • blogpost.service.test.js
    • ...etc
  • utils
    • utils.js
    • utils.test.js

Not every project is organized like the above by function/layer (middleware, controllers, data layer code, services, etc), some are organized by feature, like so:

  • features
    • blogpost
      • blogpost.controller.js
      • blogpost.db.js
      • blogpost.service.js
      • ...etc
    • admin
    • user
    • editor

In this case you could also replicate the project directory structure like:

  • tests
    • features
      • blogpost
        • blogpost.controller.test.js
        • blogpost.db.test.js
        • blogpost.service.test.js
      • admin
      • user
      • editor

But usually if a project is grouped by feature it's because the developers want all code related to a "blogpost" for example to be contained together. In which case I think it makes sense to keep the tests in the same directory, like this:

  • features
    • blogpost
      • blogpost.controller.js
      • blogpost.controller.test.js
      • blogpost.db.js
      • blogpost.db.test.js
      • blogpost.service.js
      • blogpost.service.test.js
      • ...etc
    • admin
    • user
    • editor

Differences between the two

Based on the above we can see visually the difference in the way the code and tests are organized. But how do you choose one over the other? I don't actually think there is a right or wrong way, but there are some considerations.

The nice thing about having your unit test files right next to the code files they're testing is it's less searching to do. The file is right there. On the other hand, most IDE's make it very easy to search for a specific file - usually with a hotkey command - so assuming the test file is named similarly to the code file, it's only a couple keystrokes to find the test file you're looking for.

If your project has a large number of files per directory (this is more likely the case with a structure by layer organization), then you may want to pull out all those test files into a separate, replicated test directory structure in order to make it a bit cleaner and have less IDE scrolling to do to find the code file you're looking for. I personally start out my projects with a replicated test directory structure because the projects I work on usually end up having a ton of files, and I usually structure by layer rather than feature.

I think if you're working on a small project - or you're structuring by feature - then it may make the most sense to have the unit test files next to the code files.

Depending on the test library/runner you use, it should in theory work with unit tests in a replicated test directory versus the test files being next to the code files. But I'm not sure how all test runners out there work, so want to point it out as something to consider. For example, with Mocha you can just configure the test command to differently based on the approach you take, but I don't know if with Jest/Tape/etc if that's more difficult to do.

Lastly, you may be working on a Node module instead of a REST API, and "features" or "layers" (in the context of a REST API) may not apply. With modules I think there is even less of a hard rule, but I tend to put everything in a top-level tests folder. And then you can organize sub-directories within that folder however you want. That way when you do a production build for your module, it's slightly easier to build it by just specifying a single folder - tests - be excluded from the module output (you don't want tests in your module for consumption).

Regardless of the approach you take, a key thing to remember is that it's not very difficult to refactor to move the tests around if you want to change approach. Certainly much easier than refactoring your project structure.

Integration and end-to-end tests

Because integration tests by definition test "integration" between various units, you're usually testing cases that hit code that spans across multiple files. Thus, I don't think the above approach with unit test files works for these tests.

Instead, I like having a top-level directory named "integration-tests" that contains these tests. Pretty simple. And from there you can create more sub-directories within that folder for better grouping if you want to. But I don't think there is a hard and fast rule with that.

Summary

This post is meant to be a discussion of different approaches for unit tests rather than a "this is the correct way" recommendation (although I do think there is a single way for integration tests as described above). Where you put your tests depend a lot on your project size and how it's organized (i.e. - by layer or by feature). You can use the above examination in order to help you make the best decision for your project.

At a high-level I touched on project structure for REST APIs. If you want a detailed explanation of how I usually structure my Node REST APIs, sign up below for a post that does that, and a repo that contains that structure.

Subscribe for the blogpost and repo!

No spam ever. Unsubscribe any time.