Our tests used to be full of hundreds of random ids like 5233ac4c7220e9000000000d
, 642c65770f6aa00887d97974
, 53c450afac801fe5b8000019
that all had relationships to one another. 5233ac4c7220e9000000000d
was a school, right? Was 642c65770f6aa00887d97974
a parent in that school? What if I needed a teacher and student in that school too—how did I find appropriate test entities? It wasn't an insurmountable problem—you could either poke around the CSVs that generated our fixtures or query the local database—but it slowed down writing and maintaining tests. Some engineers even had a few of these ids memorized; they'd say thing like "Oh, but 000d
is parent4! Let's use 002f
instead. They're in a school." or "I quite like 9797
, it's a solid class for story-related tests."
To make navigating our fixture IDs and their relationships a bit simpler, I wrote a simple script to query our database and decorate names for these fixture ids (e.g., student2Id
, school5Id
) with the most common relationships for that entity. For a school, we show teachers, parents, students, and classes. For a parent, we show children, teachers, schools, classes, and message threads.
/**
* - name: Student TwoParents
* - **1 current classes**: classroom3Id
* - **0 past classes**:
* - **2 parents**: parent10Id, parent11Id
* - **1 current teachers**: teacher1Id
* - **schoolId**: none
*/
export const student5Id = "57875a885eb4ec6cb0184d68";
Being able to write a line like import { student5Id } from "../fixtureIds";
and then hover over it and see that if we wanted a parent for that student, we could use parent10Id
, makes writing tests a bit more pleasant. The script to make generate this fixture-id file was pretty straightforward:
- Get ordered lists of all of the entities in our system and assign them names like
parent22Id
orteacher13Id
[^1] - Set up a map between an id like
57875a885eb4ec6cb0184d68
andstudent5Id
. - For each entity type, write model queries to get all of the relationship IDs that we're interested in.
- Use JS template strings to create nicely formatted JSDoc-decorated strings and write those strings to a file.
One small pain point I ran into was migrating all of our existing test code to reference these new fixture IDs. I wrote a script to find lines like const X = /[0-9a-f]{24}/;
, delete those lines, update the variable name with the fixture-id name from the file, and then add an appropriate import statement to the top of the file. (Shell patterns for easy automated code migrations talks through patterns I use to do migrations like this one.)
After setting up these initial fixtureId JSDoc comments, we've added JSDoc comments for more and more of our collections; it's proven to be a useful tool. We also set up a complementary fixtureEntities
file that exports the same information as TS documents so that it's straightforward to programmatically find appropriate entities. All in all, it's made our test code nicer to work with—I just wish we'd made the change sooner!
[^1]: Whenever we add any new IDs to our fixtures, we need to make sure that they come after the most recent fixtureID. Otherwise we'll end up with fixtureID conflicts!
A former teacher, Will is an engineering manager focused on database performance, team effectiveness, and site-reliability. He believes most problems can be solved with judicious use of `sed` and `xargs`.