В модульных тестах Jasmine: Не удается разрешить все параметры для TestFormInputComponentBase - PullRequest
0 голосов
/ 17 января 2019

Я новичок в модульном тестировании приложения Angular и пытаюсь протестировать свой первый компонент. На самом деле, я пытаюсь протестировать абстрактный базовый класс, который используется фактическими компонентами, поэтому я создал в своей спецификации простой Компонент и использую его для его тестирования. Но есть зависимость для обработки (Injector), и я не заглушаю ее правильно, потому что, когда я пытаюсь запустить тест, я получаю эту ошибку:

Can't resolve all parameters for TestFormInputComponentBase

Но я не уверен, что я пропустил? Вот спецификация:

import { GenFormInputComponentBase } from './gen-form-input-component-base';
import { Injector, Component } from '@angular/core';
import { TestBed } from '@angular/core/testing';

// We cannot test an abstract class directly so we test a simple derived component
@Component({
    selector: 'test-form-input-component-base'
})
class TestFormInputComponentBase extends GenFormInputComponentBase {}

let injectorStub: Partial<Injector>;

describe('GenFormInputComponentBase', () => {
    let baseClass: TestFormInputComponentBase;
    let stub: Injector;

    beforeEach(() => {
        // stub Injector for test purpose
        injectorStub = {
            get(service: any) {
                return null;
            }
        };

        TestBed.configureTestingModule({
            declarations: [TestFormInputComponentBase],
            providers: [
                {
                    provide: Injector,
                    useValue: injectorStub
                }
            ]
        });

        // Inject both the service-to-test and its stub dependency
        stub = TestBed.get(Injector);
        baseClass = TestBed.get(TestFormInputComponentBase);
    });

    it('should validate required `field` input on ngOnInit', () => {
        expect(baseClass.ngOnInit()).toThrowError(
            `Missing 'field' input in AppFormInputComponentBase`
        );
    });
});

Это класс GenFormInputComponentBase, который я пытаюсь проверить:

import { Input, OnInit } from '@angular/core';
import { FormGroup } from '@angular/forms';
import { GenComponentBase } from './gen-component-base';

export abstract class GenFormInputComponentBase extends GenComponentBase
    implements OnInit {
    @Input() form: FormGroup | null = null;
    @Input() field: string | null = null;

    @Input() label: string | null = null;
    @Input() required: boolean | null = null;

    @Input('no-label') isNoLabel: boolean = false;

    ngOnInit(): void {
        this.internalValidateFields();
    }

    /**
     * Validates that the required inputs are passed to the component.
     * Raises clear errors if not, so that we don't get lots of indirect, unclear errors
     * from a mistake in the template.
     */
    protected internalValidateFields(): boolean {
        if (null == this.field) {
            throw Error(`Missing 'field' input in AppFormInputComponentBase`);
        }

        if (null == this.label && !this.isNoLabel) {
            throw Error(
                `Missing 'label' input in AppFormInputComponentBase for '${
                    this.field
                }'.`
            );
        }

        if (null == this.form) {
            throw Error(
                `Missing 'form' input in AppFormInputComponentBase for '${
                    this.field
                }'.`
            );
        }

        return true;
    }
}

И GenComponentBase имеет зависимость, которую я пытаюсь заглушить

import { Injector } from '@angular/core';
import { LanguageService } from 'app/shared/services';

declare var $: any;

export abstract class GenComponentBase {
    protected languageService: LanguageService;

    constructor(injector: Injector) {
        this.languageService = injector.get(LanguageService);
    }

    l(key: string, ...args: any[]) {
        return this.languageService.localize(key, args);
    }
}

Любая помощь будет принята с благодарностью. Спасибо!

Обновление:

Добавив конструктор к TestFormInputComponentsBase, я могу заглушить LanguageService, и он отлично работает. Но если я попытаюсь заглушить Injector, он будет проигнорирован и все равно попытается использовать настоящий инжектор.

@Component({})
class TestFormInputComponent extends GenesysFormInputComponentBase {
    constructor(injector: Injector) {
        super(injector);
    }
}

describe('GenesysFormInputComponentBase (class only)', () => {
    let component: TestFormInputComponent;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                TestFormInputComponent,
                {
                    provide: Injector,
                    useObject: {}
                }
            ]
        });

        component = TestBed.get(TestFormInputComponent);
    });

    it('should validate required field inputs on ngOnInit', () => {
        expect(() => component.ngOnInit()).toThrowError(
            `Missing 'field' input in GenesysFormInputComponentBase.`
        );
    });
});

Я ожидаю получить ошибку из-за того, что предоставленный инжектор макета / заглушки является пустым объектом. Но я получаю ошибку от настоящего инжектора. Может ли инжектор просто не поддразнивать?

    Error: StaticInjectorError(DynamicTestModule)[LanguageService]: 
    StaticInjectorError(Platform: core)[LanguageService]: 
    NullInjectorError: No provider for LanguageService!

Ответы [ 2 ]

0 голосов
/ 22 января 2019

Есть много разных подходов к этому, но вы можете заглушить его прямо при вызове super() в вашем TestFormInputComponent, например так:

class TestFormInputComponent extends GenFormInputComponentBase {
      constructor() {
          let injectorStub: Injector = { get() { return null } };
          super(injectorStub);
    }
}

Кроме того, вам необходимо изменить способ проверки на ошибку, выданную в функции. Смотрите подробное обсуждение здесь . Как вы можете видеть в этом обсуждении, есть много способов сделать это, вот простой, использующий анонимную функцию:

it('should validate required `field` input on ngOnInit', () => {
    expect(() => baseClass.ngOnInit()).toThrowError(
        `Missing 'field' input in AppFormInputComponentBase`
    );
});

Вот рабочий StackBlitz , который показывает это работает. Я также добавил еще один тест, чтобы показать безошибочную инициализацию.

Надеюсь, это поможет!

0 голосов
/ 21 января 2019

Вы хотите протестировать GenFormInputComponentBase, так почему бы не протестировать его без TestFormInputComponent

   TestBed.configureTestingModule({
        declarations: [
            GenFormInputComponentBase,
        ],
        providers: [
          {
                provide: LanguageService,
                useValue: {}
          }
        ]
    });

Или с поставщиками LanguageService выглядит так:

        providers: [
          LanguageService,
          {
                provide: Injector,
                useValue: {}
          }
        ]
...