wake-up-neo.com

Angular Testfehler - NullInjectorError: Kein Anbieter für TrimInputDirective

Ich habe eine Angular -Direktive erstellt, die CSS-Selektoren zum automatischen Trimmen von Eingaben in meiner Anwendung verwendet.

import { Directive, HostListener, forwardRef } from '@angular/core';
import { DefaultValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

export const TRIM_VALUE_ACCESSOR: any = {
  provide: NG_VALUE_ACCESSOR,
  useExisting: forwardRef(() => TrimInputDirective),
  multi: true
};

/**
 * The trim accessor for writing trimmed value and listening to changes that is
 * used by the {@link NgModel}, {@link FormControlDirective}, and
 * {@link FormControlName} directives.
 */
/* tslint:disable */
@Directive({
  selector: `
    input
    :not([type=checkbox])
    :not([type=radio])
    :not([type=password])
    :not([readonly])
    :not(.ng-trim-ignore)
    [formControlName],

    input
    :not([type=checkbox])
    :not([type=radio])
    :not([type=password])
    :not([readonly])
    :not(.ng-trim-ignore)
    [formControl],

    input
    :not([type=checkbox])
    :not([type=radio])
    :not([type=password])
    :not([readonly])
    :not(.ng-trim-ignore)
    [ngModel],

    textarea
    :not([readonly])
    :not(.ng-trim-ignore)
    [formControlName],

    textarea
    :not([readonly])
    :not(.ng-trim-ignore)
    [formControl],

    textarea
    :not([readonly])
    :not(.ng-trim-ignore)[ngModel],
    :not([readonly])
    :not(.ng-trim-ignore)
    [ngDefaultControl]'
  `,
  providers: [ TRIM_VALUE_ACCESSOR ]
})
/* tslint:enable */
export class TrimInputDirective extends DefaultValueAccessor {

  protected _onTouched: any;

  /**
   * ngOnChange - Lifecycle hook that is called when any data-bound property of a directive changes.
   * @param {string} val - trim value onChange.
   */
  @HostListener('input', ['$event.target.value'])
  public ngOnChange = (val: string) => {
    this.onChange(val.trim());
  }

  /**
   * applyTrim - trims the passed value
   * @param {string} val - passed value.
   */
  @HostListener('blur', ['$event.target.value'])
  public applyTrim(val: string) {
    this.writeValue(val.trim());
    this._onTouched();
  }

  /**
   * writeValue - trims the passed value
   * @param {any} value - passed  value.
   */
  public writeValue(value: any): void {
    if (typeof value === 'string') {
      value = value.trim();
    }

    super.writeValue(value);
  }

  /**
   * registerOnTouched Registers a callback function that should be called when the control receives a blur event.
   * @param {function} fn - The user information.
   */
  public registerOnTouched(fn: any): void {
    this._onTouched = fn;
  }
}

Nun, da ich ein guter Entwickler bin, muss ich einige Komponententests machen ... also fange ich an, eine Datei zusammenzustellen, hier ist sie

import {Component} from '@angular/core';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {TrimInputDirective} from './trim-input.directive';

import {expect} from 'chai';

@Component({
  selector: 'my-directive-test-component',
  template: ''
})
class TestComponent {
}

describe('Trim Directive', () => {
  let fixture: ComponentFixture<TestComponent>;
  let inputDebugElement: any;
  let directive: TrimInputDirective;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestComponent,
        TrimInputDirective
      ],
      providers: []
    }).overrideComponent(TestComponent, {
      set: {
        template: '<input type="text">'
      }
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(TestComponent);
      fixture.detectChanges();
      inputDebugElement = fixture.debugElement.query(By.css('input'));
      directive = inputDebugElement.injector.get(TrimInputDirective);
    });
  }));

  it('should trim the input', () => {
    directive.ngOnChange('     1234.56     ')
    expect('1234.56').to.be('1234.56'); // I know this isn't the correct test... I will amend this
  });
});

Jetzt möchte ich meine Tests nur ausführen, um sicherzustellen, dass das Setup in der Spezifikationsdatei korrekt ist. Ich erhalte jedoch die folgende Fehlermeldung:

HeadlessChrome 0.0.0 (Mac OS X 10.12.6) Trim-Direktive "vor jedem" Haken für "sollte die Eingabe abschneiden" FAILED Nicht erfasst (im Versprechen): Fehler: StaticInjectorError (DynamicTestModule) [TrimInputDirective]:
StaticInjectorError (Plattform: Kern) [TrimInputDirective]: NullInjectorError: Kein Anbieter für TrimInputDirective! Fehler: StaticInjectorError (DynamicTestModule) [TrimInputDirective]: 

Ich verstehe nicht, warum ich diese Fehlermeldung bekomme. Warum muss ich die Direktive angeben? Ich denke nicht, dass dies notwendig ist, auch wenn ich angeben muss, was ich zur Verfügung stellen muss? Wenn die tatsächliche Direktive angegeben wird, funktioniert der Fehler nicht. Ich bin sehr verwirrt. Wenn mir jemand sagen kann, wie ich das Problem lösen kann oder warum ich es bekomme, wäre ich sehr dankbar.

Bitte beachten Sie, dass dies eine ältere Angular -App ist, die erstellt wurde, bevor die AngularCLI verfügbar war. Es ist also ein wenig unorthodox (zum Beispiel wird Jasmin nicht verwendet).

6
Mark Sandman

1) Sie müssen Ihre Anweisung nicht angeben, Sie müssen sie nur in TestingModule angeben. Dann wird es in der Vorlage mit den entsprechenden Selektoren verwendet.

2) Ihr Wähler entspricht nicht dem bei Ihrer Eingabe verwendeten. Entfernen Sie formControlName, wenn Sie alle Eingaben bestimmter Typen anwenden oder den Test ändern möchten.

input
    :not([type=checkbox])
    :not([type=radio])
    :not([type=password])
    :not([readonly])
    :not(.ng-trim-ignore)
    [formControlName],
    ^^^^^^^^^^^^^^^^^^ 

3) Die Richtlinie wird bei bestimmten Ereignissen ausgelöst. Sie müssen diese Ereignisse simulieren, um einen Effekt zu sehen. Schauen Sie sich dieses vereinfachte Beispiel an. ( Stackblitz )

@Directive({
  selector: `
    input
    :not([type=checkbox])
    :not([type=radio])
    :not([type=password])
    :not([readonly])
    :not(.ng-trim-ignore)
  `
})
export class TrimInputDirective {
  constructor(private el: ElementRef) { }

  @HostListener('blur') onLeave() {
   if (this.el.nativeElement.value)
    this.el.nativeElement.value = this.el.nativeElement.value.trim();
  }

}

Und der Test:

describe('Trim Directive', () => {
  let fixture: ComponentFixture<TestComponent>;
  let inputDebugElement: any;
  let directive: TrimInputDirective;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestComponent,
        TrimInputDirective
      ],
      imports: [FormsModule],
      providers: []
    }).overrideComponent(TestComponent, {
      set: {
        template: '<input type="text">'
      }
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(TestComponent);
      inputDebugElement = fixture.debugElement.query(By.css('input')).nativeElement;
                                                                      ^^^^^^^^^^^^
    }); 
  }));

  it('should trim the input', () => {
    inputDebugElement.value = '     1234.56     ';
    inputDebugElement.dispatchEvent(new Event('blur'));
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    fixture.detectChanges();
    expect(inputDebugElement.value).toBe('1234.56'); 
  });
});
2
Kim Kern

Sie müssen eine Direktive in Ihrer TestComponent angeben

@Component({
  selector: 'my-directive-test-component',
  template: '',
  providers: [ TrimInputDirective ]
})
class TestComponent {
}
0
sriharsha_bhat