Reputation: 8578
Lets say I have a simple module AppModule
which has many imports, declarations and providers. Now I want to write a test for a component ListComponent
which is located in this module's declaration list. ListComponent
itself uses many, (but not every) import of the AppModule
. I do it like this:
import { TestBed } from '@angular/core/testing';
// +same copy-pasted list of imports from `AppModule`
beforeEach(done => {
TestBed.configureTestingModule({
imports: [
// +same copy-pasted list of imports from `AppModule`
],
declarations: [
// +same copy-pasted list of declarations from `AppModule`
],
providers: [
{
provide: Http,
useClass: HttpMock,
},
{
provide: Router,
useClass: RouterMock,
}
// +same copy-pasted list of providers from `AppModule`
]
});
It works, but surely it is an incorrect approach. I do not want to copy-paste so much. Maybe I can reuse the AppModule in some convenient approach? Pseudocode would be like:
let appModule = new AppModule();
beforeEach(done => {
TestBed.configureTestingModule({
imports: appModule.imports,
declarations: appModule.declarations,
providers: [...appModule.providers,
{
provide: Http,
useClass: HttpMock,
},
{
provide: Router,
useClass: RouterMock,
}
]
});
But I just do not know/cannot find the syntax for such approach :(
Upvotes: 11
Views: 10318
Reputation: 792
angular 15:
app-testing.module
@NgModule({
imports: [
ModalModule.forRoot(),
NoopAnimationsModule,
HttpClientTestingModule,
TranslateTestingModule,
RouterTestingModule
],
providers: [
{ provide: ModalService, useClass: MockedModalService },
],
})
export class AppTestingModule {
}
some-component.spec.ts
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
AppTestingModule,
SomeComponentModule,
],
}).compileComponents();
});
Upvotes: 0
Reputation: 2823
You can avoid providing long list of nested services and dependencies by created a global TestBed.
While creating constant arrays for providers and imports is one way to go about this, I wanted to take this to the next step and configure the TestBed on a global level to avoid importing repetitive modules.
In order to configure a global TestBed I created a common testing module which has the utility method to configure the testing bed. This method can then be reused across all the spec files.
public static setUpTestBed = (TestingComponent: any) => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
ReactiveFormsModule,
...
],
providers: [
...
],
declarations: [TestingComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
}
CommonTestingModule: Contains utility method for creating the testing bed.
LoginComponent: login.component.spec.ts -> references to the utility method
CommonTestingModule.setUpTestBed(LoginComponent);
The full component is given below for reference:
CommonTestingModule:
import { ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
import { DatePipe } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { UtilityService } from '../services/utility.service';
import { TestBed } from '@angular/core/testing';
@NgModule({
declarations: []
})
export class CommonTestingModule {
public static setUpTestBed = (TestingComponent: any) => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
ReactiveFormsModule,
FormsModule,
HttpClientTestingModule,
RouterTestingModule,
... //other imports
],
providers: [
DatePipe,
UtilityService,
... //other imports
],
declarations: [TestingComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
}
}
And then in all your component spec files you can now reference to the utility method CommonTestingModule.setUpTestBed() which accepts the calling component name as the input parameter.
login.component.spec.ts
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { CommonTestingModule } from 'src/app/testing/common-testing.module';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
CommonTestingModule.setUpTestBed(LoginComponent);
beforeEach(() => {
// create component and test fixture
fixture = TestBed.createComponent(LoginComponent);
// get test component from the fixture
component = fixture.componentInstance;
component.ngOnInit();
});
it('Component instantiated successfully', () => {
expect(component).toBeTruthy();
});
});
That's it. You can now reuse the utility method in all your spec files. You can also create a beforeAll utility method if that suits you better.
Upvotes: 2
Reputation: 9402
You can create reusable const that contains the commom imports, providers from the modules you want.
for example in a app.providers.ts file you can have your providers like this:
import service1 from '.path/service/service1';
import service2 from '.path/service/service2';
export const providers = [service1, service2 ];
and for your imports in a app.imports.ts
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { Module1} from ''.path/service/module1';
export const imports= [
BrowserModule,
AppRoutingModule,
Module1
],
and on your app.module.ts and any other module you wanna use the same imports and providers you can do:
import { providers } from './app.providers';
import { imports } from './app.imports';
@NgModule({
declarations: [AppComponent],
imports: imports,
providers: providers,
bootstrap: [AppComponent]
})
You can also use the spread operator to add your unique imports to these shared imports on a specific module.
Upvotes: 15