← Return to blog in­dex

Mocking YAML im­ports in Vitest

Over in Streetmix world I’d been slowly con­vinc­ing my­self to con­vert hard-coded con­fig­u­ra­tion data from JSON to YAML, be­cause it’s an eas­ier for­mat to work with if you want to type the min­i­mum amount of key­strokes and move on with your life. Just let the com­puter do the con­ver­sion to JavaScript, ya know?

It turns out that switch­ing was­n’t hard at all. Streetmix uses Parcel for bundling and Vitest for test­ing, and both ecosys­tems sup­port YAML via plu­g­ins, which are pretty well-doc­u­mented, so I won’t re­hash those here.

This blog en­try, then, is about a prob­lem I ran into while try­ing to mock a YAML file in a test suite.

In many of our test suites, I use a dummy data file (aka a mock) in place of the orig­i­nal one. This is be­cause some of the orig­i­nal files are very large or change with some reg­u­lar­ity, which can gen­er­ate large snap­shots that break when­ever those files are mod­i­fied. I would pre­fer to keep the tests min­i­mal and con­tained, work­ing with place­holder data.

So in Vitest, one would mock an im­ported JSON file like so:

// my-module.js
import MY_DATA from './data.json'

// my-module.test.js
vi.mock('./data.json', () => ({
  default: require('./__mocks__/data.json')
}))

While data.json is real ap­pli­ca­tion data, __mocks__/data.json would be a sim­pli­fied fac­sim­ile of it — just enough to make sure our code works as in­tended un­der test.

With the YAML plu­g­ins in place, re­plac­ing the data file is easy-peasy.

// my-module.js
import MY_DATA from './data.yaml'

But, un­for­tu­nately, do­ing the same in the test suite fails.

// my-module.test.js
vi.mock('./data.yaml', () => ({
  default: require('./__mocks__/data.yaml')
}))

// --> Vitest error

So… what now?

The in­com­ing mock data was a raw YAML doc­u­ment, and not read­able by JavaScript. After a lit­tle bit of floun­der­ing and ex­per­i­men­ta­tion, I started to rec­og­nize that the YAML plu­gin only in­ter­cepted the import syn­tax from ECMAScript mod­ules, but not the CommonJS require() func­tion, even if the lat­ter was still sup­ported by Vitest.

The so­lu­tion? Replace require() with import():

vi.mock('./data.yaml', async () =>
  await import('./__mocks__/data.yaml')
)

There’s a cou­ple of im­por­tant things to point out here.

The first thing is that I’m us­ing a dy­namic im­port, which is dif­fer­ent from a top-level im­port. A dy­namic im­port looks like a func­tion and works like one too, mean­ing you can put it any­where in the mod­ule, rather than use a spe­cial de­c­la­ra­tion at the top.

So why could­n’t we just use a top-level im­port to bring it in as a vari­able, and then as­sign that vari­able to the re­turn value of the mock? That’s be­cause Vitest hoists the mock func­tion, and it can’t ac­cess any vari­ables out­side of it — and that in­cludes im­ported files. That means the import has to hap­pen in­side its fac­tory func­tion, so we use the dy­namic im­port.

Secondly, import() is an asyn­chro­nous func­tion, so the fac­tory func­tion must be pref­aced with the async key­word, and we’d need to await the re­sult of that im­port.

And that’s it. Now the YAML plu­gin takes over dur­ing im­port, and the test suite suc­ceeds.

Anyway, I wrote all this for a rea­son

I don’t have a ton of pro­gram­ming-re­lated blog posts (heck, I don’t re­ally blog much at all, just look around), so I find it a lit­tle laugh­able that this, of all the things I have to deal with in code world, is the prob­lem that mer­its one.

But, hang on, I can ex­plain my­self!

There’s a lot of de­vel­op­ers out there in this age of AI-powered every­thing that swear upon the holy name of Marc Andreesen that AI has dra­mat­i­cally ten-exxed their pro­duc­tiv­ity and out­put. As a skep­tic of early-adopter tech­nol­ogy (many of them op­er­ate as fi­nan­cial in­stru­ments whose in­vestors are paid out when boost­ers can ar­ti­fi­cially in­flate its value) I have been ob­serv­ing AI at ar­m’s length, rather than em­brac­ing it whole­sale.

I do find LLM chat­bots po­ten­tially use­ful for the next gen­er­a­tion of in­for­ma­tion dis­cov­ery, in the way that search en­gines were in the late 1990s. And, like search en­gines, our cur­rent it­er­a­tion of AI is only as use­ful as the qual­ity of in­for­ma­tion that al­ready ex­ists out in the wild. (This is some­thing many other tech writ­ers have al­ready noted; I’m hardly de­clar­ing any­thing novel here.) Initially, I did­n’t find any­thing re­lated in the plu­gin repos­i­to­ry’s is­sues or dis­cus­sions, so I went to GitHub Copilot to see if it might have scraped some­thing some­where else:

I asked Copilot how to import a mock YAML file in Vitest, and here's what it said.

I’ll cut to the chase: it did­n’t know. It had no idea! As far as this model was con­cerned, there just is­n’t any doc­u­men­ta­tion that matched my spe­cific us­age.

To its credit, Copilot tried to be help­ful, as in why don’t you just write out its con­tents in­line as a JS ob­ject?” But that’s not what I wanted to do! (And the less said about mock the en­tire plu­gin, the bet­ter.) Now, I’ve been pro­gram­ming long enough to know that you should­n’t just blindly ac­cept any an­swer to a ques­tion. Thankfully, I also have a good enough prob­lem-solv­ing sense, so with a lit­tle bit of ad­just­ment of the ol’ think­ing cap, I was able to find a bet­ter so­lu­tion.

A brief light in the dark for­est

Platforms have an in­her­ent in­cen­tive to wall off its gar­dens, keep­ing its users and any con­tent they’ve made trapped in­side. If a com­peti­tor op­er­ates a large lan­guage model trained on any data it can get ahold of, that in­cen­tive only deep­ens. And so we have a dou­ble-whammy of a sit­u­a­tion: in­for­ma­tion in­creas­ingly pro­duced only in­side of si­los which put up ever-more bar­ri­ers to ac­cess.

If I had posed my prob­lem in Vite’s Discord server, I might have ac­tu­ally re­ceived an an­swer in about the same time that it took for me to fig­ure it out on my own. But Discord is an­other walled gar­den. It is­n’t anony­mously search­able from the wider web, the way Stack Overflow is. If you ask for tech­ni­cal sup­port in Discord, the so­lu­tion dis­ap­pears into barely-ac­ces­si­ble his­tory.

And now, there’s an ac­tivist move­ment poi­son the AI well by tak­ing con­tent off the Internet, or ob­fus­cat­ing it in a way to make it use­less to those mod­els. This ac­tu­ally makes a lot of sense to me as a re­sis­tance move. After all, peo­ple have been creating” con­tent, of­ten un­paid, for decades, and cor­po­ra­tions profit hand­somely from it.

So all of this is a down­ward spir­ial into the Dark Forest the­ory of the Internet: where most of the real peo­ple are quiet and in­su­lar, and all of the chat­ter are bots.

It’s a shitty out­come. And that’s an Internet in­ca­pable of help­ing me solve a prob­lem when I needed to im­port a YAML file into Vitest.

So here’s a blog post. It’s not on a plat­form, and not in a walled gar­den. I don’t re­ally care about SEO, but I hope the search en­gines find it any­way. And I hope the AI mod­els do too. Get it in there. Maybe some­day some­one else will ask a sim­i­lar ques­tion, and next time, the bot will know.


Published on 24 April, 2025.