Skip to main content

Testing

It's very likely that consumers will run into all sorts of edge-cases when running your codemod. That's why it's important to always start by writing tests (TDD style) to assert its behavior. Think about a start and end state. How you might be able to achieve that? What edge-cases can potentially arise?

CodeshiftCommunity (and jscodeshift) exposes testing utilities to help.

Codemods are a textbook example of where TDD works. You have an input file, you run the script and you get output. Thus I would really recommend using TDD for codemod projects. Not only does it make codemods more stable, but having projects with test workflow setup, will help you learn. Because you can experiment just by running the same test over and over again.

Reference

Folder structure

If you're planning to publish a codemod to the public registry, follow the Authoring guide.

Once you've initialized, your file structure should look something like this:

community/[package-name]/[version]
/transform.ts
/transform.spec.ts // Here's where your test should go

An example

To give an example, consider you're trying to write a codemod that removes deprecated props from a component. In this case, you would be able to write a simple test using jest & @codeshift/test-utils like so..

import * as transformer from '../transform';
import { applyTransform } from '@codeshift/test-utils';

it('should remove all deleted props', async () => {
const result = await applyTransform(
transformer,
`
import Foo from '@mylib/foo';

const App = () => <Foo isSelected doNotUse='true' />;
`,
{ parser: 'tsx' },
);

expect(result).toMatchInlineSnapshot(`
"
import Foo from '@mylib/foo';

const App = () => <Foo isSelected />;"
`);
});

Great, but what have we missed? What might go wrong?

Considerations

Whenever you're writing tests for a codemod, it's usually good to consider a few classic gotchas.

In general they can include:

  • Import aliasing: import { Foo as Bar } from '@mylib/foo';
  • Idempotency: Will a codemod produce the same result if run multiple times on the same file
  • Indirection: Is the code your modifying being obscured by indirection?
  • Equivalent syntax: function foo() {} vs const foo = () => {}