Skip to content

late-warrior/nodejs-ts-test-setup

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NodeJS ESM Typescript Test setup

c8 nyc mocha typescript ts-node

A minimal Typescript setup that demonstrates unit testing in NodeJS and generating coverage. Since there are so many moving parts in the test ecosystem, this is an attempt to isolate them separately and debug them individually

Useful scripts

  • yarn test - tests with coverage by c8 - coverage reporter in c8-coverage folder
  • yarn test:nyc - tests wth coverage by nyc - numbers not accurate, attempted fix - yarn test:nyc:fix does not work as well
  • yarn test:watch - does not work yet - limitation of mocha + ESM
  • yarn test-only - run tests without coverage

Concepts

TODO: Diagram to explain how all this fits in together comes here

Compilation flags (in tsconfig.json)

  • Specifying module is the most important - this governs how the output transpiled file will look like
  • target is useful for any feature replacement if need be
  • sourceMap is useful for debbugers

Build time vs on-the-fly compilation

  • Build time compilation is the invocation of the typescript compiler through an npm script like yarn compile. The compiled files are written to the dist directory (or configured outDir in tsconfig.json)
  • on-the-fly compilation is achieved by require hooks - ts-node intercepts every import request and compiles the imported module. It internally uses the same tsconfig.json file but does not write to dist - instead it uses a temporary directory and cleans it up soon after the module is loaded into memory

Enabling ESM in TS

  • Tests are driven by mocha
  • mocha allows for specifying hooks that it passes on to nodejs - we specify the ts-node/esm hook to facilitate on-the-fly compilation of imports in test files
  • See .mocharc.cjs for full configuration used

Enabling coverage

  • Coverage is usually one layer above tests
  • You can generate coverage in 2 ways -
    • Instrument your source and run your tests on the instrumented source as two separate steps
    • On the fly instrumentation by specifying a nodejs require hook (sounds familiar, doesn't it ?)
  • Coverage is generated by nyc - for typescript, extend from the @istanbuljs/nyc-config-typescript
  • [Not working] Coverage numbers are wrong - the @istanbuljs/esm-loader-hook needs to be used, but I could not figure out a way to make it work. See issue
  • Coverage can also be generated via c8 which used nodejs' native coverage support - this is far simpler to configure - see .c8rc.json

Alternative coverage approach

  • Add something like - "instrument": "nyc instrument src in-src", to your package.json to instrument
  • Modify your imports under the test directory to import from in-src wherever you import from src
  • Remove on-the-fly instrumentation (can run tests directly, instead of through nyc - i.e yarn test-only)

Features and Limitations

  • Write source and tests in typescript
  • Generate accurate coverage information with c8
  • Watch tests (not working) - ESM watching is not supported in mochajs yet
  • Coverage information generated with nyc not very accurate

Errors encountered

A sampling of errors encountered - goes to show the many pieces of the puzzle here -

  • TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "file:///add_test.ts" This happens if you don't teach mocha about how to load ESM modules - can be fixed by the "loader": "ts-node/esm" in .mocharc.cjs
  • ReferenceError: exports is not defined in ES module scope - this happens when typescript transpiles your code to commonjs format and includes an exports object - since this code is subsequently imported by a test through an ESM loader, it breaks
  • Module is not defined in ES module scope - fix is to rename nyc.config.js to nyc.config.cjs
  • We need to hook in the @istanbuljs/esm-loader-hook for nyc to generate the right coverage stats, but that fails with [ERR_UNKNOWN_FILE_EXTENSION]. It looks like setting this option for nyc disables the "experimental-specifier-resolution": "node" that we set for mocha (in .mocharc.cjs). Run test:nyc:fix to reproduce this

About

Basic setup for running tests in nodejs

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published