Search code examples
angularunit-testingtestingdependency-injectionmodule

Angular: How to create a testing-module for a feature in an Angular app?


I have a "normal" (nothing special about it) Angular app with some shared features. Since i want to avoid mocking these shared-features in component tests over and over I was thinking about introducing a test-module version of every feature. It should look like something like this:

SharedModule
 |
 |- drop-zone
 |   |_ (module contents skipped for brevity)
 |   |_ drop-zone.module.ts
 |   |_ drop-zone-testing.module.ts
 |
 |- other-features...
  • The drop-zone.module.ts should be an angular module, including all "prod / normal" declarations, services and so on.
  • The drop-zone-testing.module.ts should also include the "normal" declarations but fake services for example.

But when doing so, I get the Angular error when running ng build:

The Component 'FileDropZoneComponent' is declared by more than one NgModule.
- 'FileDropZoneComponent' is listed in the declarations of the NgModule 'FileDropTestingModule'.
- 'FileDropZoneComponent' is listed in the declarations of the NgModule 'FileDropModule'.

I already tried to remove all testing-module-files from the angular-build via pattern *testing.module.ts in tsconfig's exclude, but to no avail :/ I thought that excluding these modules from build should stop angular from complaining about two modules being present with the same declaration. But it does not work.

Does anyone have a solution how to create TestingModules for a custom feature in an Angular app?


Solution

  • I figured out how to do it - but it seems like a workaround that does not even make too much sense to me :D But it works, both in tests and on build:

    I should mention that in my solution I still removed the file, where I define my own decorator in via tsconfig.app.json:

    {
       "exclude": ["src/**/testing/**/*"],
    }
    
    1. Create your own decorator that simply wraps the NgModule decorator:
    // e.g. src/shared/testing/utils.ts
    export function NgTestingModule(obj: NgModule) {
      return NgModule(obj);
    }
    
    1. use in your testing modules:
    import { NgTestingModule } from '../testing/utils';
    
    @NgTestingModule({ ... })
    export class DropZoneTestingModule {}
    
    1. and in your tests:
    TestBed.configureTestingModule({
      imports: [DropZoneTestingModule],
    });