I'm trying to write a library to help writing unit tests, and I'm facing a really strange bug: the TestBed and ComponentFixture classes used by the library are different from the TestBed and ComponentFixture classes used in the tests of the external app using the library.
The two projects are separate: the app uses yarn link testlib to use the testlib library, since it's not published anywhere yet.
The bug does not happen when the lib and the app are in the same Angular project.
```Angular CLI: 6.0.3
Node: 8.11.1
OS: darwin x64
Angular: 6.0.3
... animations, cli, common, compiler, compiler-cli, core, forms
... http, language-service, platform-browser
... platform-browser-dynamic, router
@angular-devkit/architect 0.6.3
@angular-devkit/build-angular 0.6.3
@angular-devkit/build-optimizer 0.6.3
@angular-devkit/core 0.6.3
@angular-devkit/schematics 0.6.3
@ngtools/webpack 6.0.3
@schematics/angular 0.6.3
@schematics/update 0.6.3
rxjs 6.2.0
typescript 2.7.2
webpack 4.8.3
### Repro steps
* configure the CLI to use yarn
* create a new project: ng new project1
* inside project1, create a library: ng generate library testlib
* add the following class to the library, make sure it's part of the public api:
import { ComponentFixture, TestBed } from '@angular/core/testing';
export class LibTest {
test(arg: any, bed: any) {
console.log('Is arg instanceof ComponentFixture: ', (arg instanceof ComponentFixture));
console.log('bed: ', bed);
console.log('bed === TestBed ', bed === TestBed);
}
}
* Build the library: `ng build testlib --prod`
* link the built library: `cd dist/testlib; yarn link`
* Elsewhere, create a new app: `ng new myapp`
* Inside myapp, add testlib by linking it: `yarn link "testlib"`
* Edit the app.component.spec.ts file, and add the following:
import { LibTest } from 'testlib';
[...]
it('should share ComponentFixture and TestBed with testlib', () => {
const fixture = TestBed.createComponent(AppComponent);
new LibTest().test(fixture, TestBed);
});
* Execute the test: `ng test`
### Observed behavior
LOG: 'Is arg instanceof ComponentFixture: ', false
LOG: 'bed: ', function TestBed() { ... }
LOG: 'bed === TestBed ', false
### Desired behavior
LOG: 'Is arg instanceof ComponentFixture: ', true
LOG: 'bed: ', function TestBed() { ... }
LOG: 'bed === TestBed ', true
```
IIRC this is because CLI resolves imports in linked files relative to the original location of the file (as opposed to the link location). So for the library code resolution algorithm will find @angular/core package in the project1/node_modules/@angular/core folder and for the application it will find it in myapp/node_modules/@angular/core. As these are two completely independent packages, you get the described behavior.
The good new is that it will work correctly with real package installation if @angular/core is specified as a peerDependency.
The bug does not happen when the lib and the app are in the same Angular project.
Because in this case TypeScript paths options is used to link library instead of real symlinks.
Maybe setting preserveSymlinks: true in the build configuration used by tests can solve this issue? If it's even possible at the moment. This option controls resolve.symlinks in the underlying WebPack configuration, so should theoretically solve the problem.
Thanks A LOT @devoto13. You rock!
These three solutions indeed solve the issue:
ng test --preserve-symlinksprojects.<app>.test.options, add the option "preserveSymlinks": true. That solves my issue, but I still think an explanation about this would be nice to have in the story about libraries, because having to do that is far from obvious (to me at least), and using yarn link (or npm link), AFAIK, is the standard way to test a library that is under development.
Thanks again.
@jnizet Happy it helped you to solve the problem!
Also, and tangentially, node itself will resolve in the same way and also has a similar symlink option. (Not needed in this situation though)
And, yes, the linked library story should be updated to reflect this flags use.
Closing this issue as resolved. Thanks @devoto13
This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.
Read more about our automatic conversation locking policy.
_This action has been performed automatically by a bot._
Most helpful comment
IIRC this is because CLI resolves imports in linked files relative to the original location of the file (as opposed to the link location). So for the library code resolution algorithm will find
@angular/corepackage in theproject1/node_modules/@angular/corefolder and for the application it will find it inmyapp/node_modules/@angular/core. As these are two completely independent packages, you get the described behavior.The good new is that it will work correctly with real package installation if
@angular/coreis specified as apeerDependency.Because in this case TypeScript
pathsoptions is used to link library instead of real symlinks.Maybe setting
preserveSymlinks: truein the build configuration used by tests can solve this issue? If it's even possible at the moment. This option controls resolve.symlinks in the underlying WebPack configuration, so should theoretically solve the problem.