typescript - Hur man använder `import.meta` vid testning med Jest


Jag skriver Node.js-kod (i TypeScript) med ESModules och jag behöver åtkomst till __dirname. För att få åtkomst till ESM-ekvivalenten av __dirname i CommonJS, anropar jag dirname(fileURLToPath(import.meta.url)). Jag skriver också tester i Jest med TypeScript. Med hjälp av denna guide satte jag upp Babel. När jag kör kommandot jest får jag

const DIRNAME = (0, _path.dirname)((0, _url.fileURLToPath)(import.meta.url));
                                                                      ^^^^
SyntaxError: Cannot use 'import.meta' outside a module

Här är filerna jag skrev

someCode.ts:

import { dirname } from "path";
import { fileURLToPath } from "url";

const DIRNAME = dirname(fileURLToPath(import.meta.url));

export const x = 5;

someCode.test.ts:

import { x } from "./someCode";

it("Returns 5 as the result", () => {
    expect(x).toEqual(5);
});

.babelrc.json:

{
    "presets": [
        ["@babel/preset-env", { "targets": { "node": "current" } }],
        "@babel/preset-typescript"
    ]
}

tsconfig.json:

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "node"
    },
    "include": ["./src/**/*.ts", "./src/**/*.js"]
}

package.json:

{
    "name": "test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "type": "module",
    "scripts": {
        "test": "jest"
    },
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "@babel/core": "^7.12.7",
        "@babel/preset-env": "^7.12.7",
        "@babel/preset-typescript": "^7.12.7",
        "jest": "^26.6.3",
        "typescript": "^4.1.2"
    }
}

Miljö:

  • Nod: 14.1.0
  • Se package.json för modulversioner

Svar #1

Vi använder import.meta.url för att använda webbarbetare, till exempel genom att använda det inbyggda stöd för arbetare i Webpack 5. Det fungerar utmärkt men misslyckas när vi kör Jest-testerna.

babel-vite-preset hanterar inte detta såvitt jag kan se. Det bästa svaret om att extrahera och mocka koden som använder import.meta fungerar, men är besvärligt.

Jag har konstaterat att den för närvarande bästa lösningen är att använda babel-plugin-transform-import-meta. Denna enkla plugin ersätter import.meta.url-syntaxen med Node.js-kod för att hämta URL:en till den aktuella filen, så den fungerar bra i tester. Observera att du endast vill ha denna plugin aktiv när du kör testerna.

Svar #2

Lösningen: Modulera ESM-koden och gör en mock av den.

Jag stötte på detta problem nyligen också, och jag var tvungen att:

  1. Exportera ESM-specifika problemet, t.ex. import.meta-funktionalitet, till dess egen "utils"-fil/funktion.
  2. Skapa sedan en mockfunktion som inte kräver ESM-specifik funktionalitet i en mocks-katalog.
  3. i filerna som använder den funktionaliteten, deklarera jest.mock("/path/to/that/file.ts")

Processen kommer att se ut ungefär så här:

Den ursprungliga filstrukturen

src/
--/someCode.ts
--/__tests__/
/--/--/someCode.test.ts
// someCode.ts
...
const getApiUrl = () => { 
  ...
  const url = import.meta.env.SOME_ENV_VAR_HERE;
  ...
  return url;
};
...

Den nya filstrukturen

src/
--/someCode.ts
--/utils.js
--/__mocks__/
--/--/utils.ts
--/__tests__/
--/--/someCome.test.ts

och sedan i själva filerna:

// someCode.ts
...
import { getApiUrl } from "./utils.ts";
...

// utils.js
export const getApiUrl = () => {...};

och för testerna:

// __mocks__/utils.ts
export const getApiUrl = jest.fn(() => 'some.url');

// __tests__/utils.test.ts
...
jest.mock("src/util.ts");
...
describe(...);

Relaterade artiklar