Search code examples
node.jstypescriptmocha.jstypescript2.0ts-node

Running tests located in a separate directory with mocha and ts-node?


I have source code and tests separated as follows:

`src/main/ts/hello.ts`  //SOURCE FILES HERE
`src/test/ts/hello.spec.ts` //SPEC FILES HERE

The import statement in src/test/ts/hello.spec.ts looks like this:

import hello from 'hello';

The hello.ts source code looks like this:

    export function hello() {
      return 'Hello World!';
    }

    export default hello;

My tsconfig.json is setup such that the test files can import source modules without using relative paths like this:

    {
       "include": [
         "src/main/ts/**/*.ts"
       ],
       "exclude": [
         "node_modules"
       ],

       "compilerOptions": {
         "experimentalDecorators": true,
         "noImplicitAny": true,
         "moduleResolution": "node",
         "target": "es6",
         "baseUrl": ".",
         "paths": {
           "*": [
             "*", "src/main/ts/*"
           ]
         }
       }
     }

This way the hello.spec.ts file can import hello using the statement import hello from 'hello';

I'm trying to run the tests with npm test configured to run mocha and tsnode like this (Based on this article):

"scripts": {
  "test": "mocha -r ts-node/register src/test/ts"
},

However it does not look like ts-node is picking up on my tsconfig.json configuration as I get this error:

mocha -r ts-node/register src/test/ts

Error: Cannot find module 'hello'
    at Function.Module._resolveFilename (module.js:336:15)
    at Function.Module._load (module.js:286:25)

Solution

  • The module resolution that you set through paths in tsconfig.json is purely an compile-time thing. (See this ts-node issue report and this TypeScript issue report for details.) It does not affect how the code is emitted, which means that your test file is doing a require("hello"), which Node cannot resolve. The consequence of paths being a compile-time thing is that your module loader needs to be configured to also perform the same kind of resolution that you specify in tsconfig.json. If you were using RequireJS, for instance, you'd need to have a configuration for it that does the same thing paths in tsconfig.json does. You are using Node, however...

    What you can do in Node is use tsconfig-paths, which will read the tsconfig.json, parse the paths setting and change the module resolution in Node so that it works.

    Using your code, I modified hello.spec.ts to have at least one test for feedback:

    import hello from "hello";
    import "mocha";
    
    it("q", () => {
        if (hello() !== "Hello World!") {
            throw new Error("unequal");
        }
    });
    

    I installed tsconfig-paths and @types/mocha (so that import "mocha" does the right thing compilation-wise in the test file I show above) and invoked Mocha like this:

    $ ./node_modules/.bin/mocha --compilers ts:ts-node/register -r tsconfig-paths/register 'src/test/ts/**/*.ts'
    

    I got this output:

      ✓ q
    
      1 passing (20ms)