Как повторно использовать весь импорт в тестовых файлах Angular

Допустим, у меня есть простой модуль AppModule, в котором много импортов, объявлений и провайдеров. Теперь я хочу написать тест для компонента ListComponent, который находится в списке объявлений этого модуля. Сам ListComponent использует многие (но не все) импортные файлы AppModule. Я делаю это так:

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`
        ]
    });

Это работает, но, безусловно, это неправильный подход. Я не хочу копипастить так много. Может быть, я могу повторно использовать AppModule каким-то удобным способом? Псевдокод будет выглядеть так:

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,
            }
        ]
    });

Но я просто не знаю/не могу найти синтаксис для такого подхода :(


person Andrius Naruševičius    schedule 14.02.2018    source источник


Ответы (2)


Вы можете создать многоразовую константу, содержащую импорт commo, провайдеров из нужных вам модулей.

например, в файле app.providers.ts ваши провайдеры могут быть такими:

import service1 from '.path/service/service1';
import service2 from '.path/service/service2';

export const providers = [service1, service2 ];

и для вашего импорта в 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
],

и в вашем app.module.ts и любом другом модуле вы хотите использовать те же импорты и провайдеры, которые вы можете сделать:

import { providers } from './app.providers';
import { imports } from './app.imports';
@NgModule({
    declarations: [AppComponent],
    imports: imports,
    providers: providers,
    bootstrap: [AppComponent]
})

Вы также можете использовать оператор распространения, чтобы добавить свой уникальный импорт к этим общим импортам в определенном модуле.

person Eduardo Vargas    schedule 14.02.2018
comment
Спасибо, это именно то, что мне было нужно. - person Andrius Naruševičius; 14.02.2018
comment
Интересно, почему такой подход не используется по умолчанию в angular? Почему для каждой спецификации нужно прописывать импорты, провайдеры, декларации? - person MeIr; 18.01.2020

Вы можете избежать предоставления длинного списка вложенных сервисов и зависимостей, создав глобальный TestBed.

Хотя создание константных массивов для поставщиков и импорта — это один из способов сделать это, я хотел перейти к следующему шагу и настроить TestBed на глобальном уровне, чтобы избежать импорта повторяющихся модулей.

Чтобы настроить глобальный тестовый стенд, я создал общий модуль тестирования, в котором есть служебный метод для настройки тестового стенда. Затем этот метод можно повторно использовать во всех файлах спецификаций.

public static setUpTestBed = (TestingComponent: any) => {
    beforeEach(() => {
      TestBed.configureTestingModule({
        imports: [
          ReactiveFormsModule,
          ...
        ],
        providers: [
          ...
        ],
        declarations: [TestingComponent],
        schemas: [CUSTOM_ELEMENTS_SCHEMA]
      });
    });
  }

CommonTestingModule: содержит служебный метод для создания испытательного стенда.
LoginComponent: login.component.spec.ts -> ссылки на служебный метод

CommonTestingModule.setUpTestBed(LoginComponent);

Полный компонент приведен ниже для справки:

Общий тестовый модуль:

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]
      });
    });
  }
}

И затем во всех ваших файлах спецификаций компонентов вы теперь можете ссылаться на служебный метод CommonTestingModule.setUpTestBed(), который принимает имя вызывающего компонента в качестве входного параметра.

логин.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();
  });

});

Вот и все. Теперь вы можете повторно использовать служебный метод во всех ваших файлах спецификаций. Вы также можете создать служебный метод beforeAll, если он вам больше подходит.

person Evan MJ    schedule 14.11.2020