nrwl / nx

Smart Monorepos · Fast CI
https://nx.dev
MIT License
23.27k stars 2.32k forks source link

Single jest unit test takes too long to initialize #14911

Closed kkoo95 closed 1 year ago

kkoo95 commented 1 year ago

Current Behavior

In an angular workspace, when running a single unit test it may takes a really long time to complete. From my investigations it seems like configureTestingModule eventually compiles (or just parse i dont really know) ALL the components which public-api.ts exports. And such barrel files are parsed because of how authored tsconfig are made and how symbols are imported into the spec files.

// tsconfig.base.json
{
  ...
  "compilerOptions": {
    ...
    "paths": {
      "@mylib/mylib": ["libs/mylib/src/index.ts"],
      "@mylib/mylib/sublib": ["libs/mylib/sublib/src/index.ts"]
    }
  }
}
// libs/mylib/src/lib/my-lib.service.spec.ts
import { MyLib2Service } from "@mylib/mylib"; // <= this import
import { MySubLibService } from "@mylib/mylib/sublib"; // <= this import

describe('MyLibServiceTest', () => {
  ...
})

Expected Behavior

Spec files shouldn't use imported symbols from "library names" but rather through a relative path. IDE shouldn't suggest to import them from mapped names neither (that's what IntelliJ currently do).

// libs/mylib/src/lib/my-lib.service.spec.ts
import { MyLib2Service } from "./my-lib2.service"; // <= this kind of import instead
import { MySubLibService } from "../../sublib/src/lib/my-lib2.service"; // <= this kind of import instead

For example, with the current project that im working on, running a single unit test can take up to 45sec with library names as import sources, whereas with direct import statements, it takes "only" 15sec (probably because of the size of my project, im not sure).

Steps to Reproduce

You'll just need a brand new angular workspace with 1 buildable library with MANY components exported from it.

Nx Report

Node : 16.13.1
   OS   : win32 x64
   npm  : 8.1.2

   nx : 15.6.3
   @nrwl/angular : 15.6.3
   @nrwl/cypress : 15.6.3
   @nrwl/detox : Not Found
   @nrwl/devkit : 15.6.3
   @nrwl/esbuild : Not Found
   @nrwl/eslint-plugin-nx : Not Found
   @nrwl/expo : Not Found
   @nrwl/express : Not Found
   @nrwl/jest : 15.6.3
   @nrwl/js : 15.6.3
   @nrwl/linter : 15.6.3
   @nrwl/nest : Not Found
   @nrwl/next : Not Found
   @nrwl/node : 13.4.3
   @nrwl/nx-cloud : 15.0.3
   @nrwl/nx-plugin : Not Found
   @nrwl/react : Not Found
   @nrwl/react-native : Not Found
   @nrwl/rollup : Not Found
   @nrwl/schematics : Not Found
   @nrwl/storybook : Not Found
   @nrwl/web : Not Found
   @nrwl/webpack : 15.6.3
   @nrwl/workspace : 15.6.3
   @nrwl/vite : Not Found
   typescript : 4.8.4

Failure Logs

No response

Additional Information

At first, my workaround was to add an empty paths mapping into tsconfig.spec.json. That way IntelliJ doesn't bother me with wrong automatic imports (or suggestions). But it doesn't work because at test runtime, when reaching imports from, say, imported components, TS can't recognize mapped imports over there anymore (dah!).

Following example DOES NOT WORK:

// libs/mylib/tsconfig.spec.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    ...
    "paths": {}
  }
  ...
}
// libs/mylib/src/lib/my.component.spec.ts 
import { MyComponent } from './my.component'; // as wished

describe('MyComponentTest', () => {
  let component: MyComponent;
  ...
})
// libs/mylib/src/lib/my.component.ts 
import { Component } from '@angular/core';
import { MySubLibService } from "@mylib/mylib/sublib";  // as wished but FAILS

@Component({ ... })
export class MyComponent {
  constructor(private svc: MySubLibService) { }
  ...
}

Maybe the solution is to have some kind of linting for imports in spec files ?

barbados-clemens commented 1 year ago

As you discovered, when you import a file, everything in that file must be evaluated and since Jest cannot understand TS/ESM you have to transform it which slows things down on the first run (with a jest cache miss) subsuquent runs will most likely have a jest cache hit so won't be as bad.

if you want to use relative imports for all of your test files you're able to do that for you workspace by configuring eslint rules. We have no control over how an editor is going to auto complete other than configuring tools like eslint and TS.

you can also allow for deep imports in your tsconfig.base.json like

// tsconfig.base.json
{
  ...
  "compilerOptions": {
    ...
    "paths": {
      "@mylib/mylib": ["libs/mylib/src/index.ts"],
      "@mylib/mylib/*": ["libs/mylib/sublib/src/*"]
    }
  }
}

which allows you to still use the alias but allows you to import only the files you want. you could also do something like @my/libs/testing/* to make it obvious that this is only for testing if you wanted and can write a custom eslint rule to make sure those imports are only used where you want them to be.

github-actions[bot] commented 1 year ago

This issue has been closed for more than 30 days. If this issue is still occuring, please open a new issue with more recent context.