Skip to content

Instantly share code, notes, and snippets.

@lhorie
Last active June 12, 2020 12:59
Show Gist options
  • Save lhorie/d7874a999e6f379a7d0f5496f50cb6c3 to your computer and use it in GitHub Desktop.
Save lhorie/d7874a999e6f379a7d0f5496f50cb6c3 to your computer and use it in GitHub Desktop.
Write better tests with one neat trick

Write better tests with one neat trick

TL:DR; assert your input

If you write non-trivial code, inevitably you'll run into a situation where setting up a test is not super readable. Maybe you have to mock a bunch of things, or the input data is a data structure that spans dozens of LOC, or maybe you are testing with a large-ish fixture that was defined in another file because linting or whatever.

Due to the nature of my job, I happen to read a lot of other people's code, so you can imagine how mildly annoying it is when I'm code reviewing something, and the test looks something like this:

test('codemod does what it needs to', async () => {
  const src = await read('fixture/src.js');
  
  const ast = parse(src);
  const modded = codemod(ast);
  const out = toString(modded);
  
  expect(out).toMatchInlineSnapshot(`
    import foo from 'foo';
    
    const myThing = async items => {
      return Promise.all(items.map(foo));
    }
    
    export {myThing};
  `)
})

Yeah, it's using jest's toMatchInlineSnapshot so I at least have an idea what the output is. Goody!

If I'm lucky, the author bothered to write a commit message with a description of what the output should look like, so I would at least be able to cross-check that the snapshot does what the author claims.

BUT. As a reader, I have no idea what the input was. I may have to scroll through various unrelated other files to find the fixture and then be scrolling back and forth to mentally understand what the difference between the two are. And that is of course, assuming that the input isn't dynamically generated like an enzyme-rendered component or whatever. Anyways. first world problems I know, but still not fun.

So what's an easy way to improve the situation?

That's right, my tldr reader friends, assert the input.

test('codemod does what it needs to', async () => {
  const src = await read('fixture/src.js');
  
+ expect(src).toMatchInlineSnapshot(`
+   import foo from 'foo';
+   
+   const myThing = items => {
+     return items.map(foo);
+   }
+   
+   export {myThing};
+ `);
+  
  const ast = parse(src);
  const modded = codemod(ast);
  const out = toString(modded);
  
  expect(out).toMatchInlineSnapshot(`
    import foo from 'foo';
    
    const myThing = async items => {
      return Promise.all(items.map(foo));
    }
    
    export {myThing};
  `)
})

Now I can glance at the test and get a one-picture-is-worth-a-thousand-words view of what the code being tested actually does, even if the author didn't bother to write a nice commit message. If you're using jest, then you don't even need to copy/paste the code into the snapshot, just slap that -u to have it be generated for you.

Revolutionary, right? Well, no. See given-when-then, arrange-act-assert, hoare triple and probably a dozen other variations in CS literature.

Liked this post? Smash that like button. Oh wait, there isn't one. Well, then go use your newly acquired grey beard superpowers for good. Ciao

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment