首页 文章

角度测试错误 - NullInjectorError:没有TrimInputDirective的提供者

提问于
浏览
6

我创建了一个Angular Directive,它使用CSS选择器自动修剪我的应用程序中的输入,它看起来像......

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

现在做一个好的开发人员我必须对一些单元测试...所以我开始把文件放在一起,就在这里

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

现在我希望运行我的测试只是为了确保spec文件中的设置是正确的,但是我收到以下错误:

HeadlessChrome 0.0.0(Mac OS X 10.12.6)修剪指令“在每个”挂钩之前“应修剪输入”FAILED未捕获(在承诺中):错误:StaticInjectorError(DynamicTestModule)[TrimInputDirective]:StaticInjectorError(Platform:core) [TrimInputDirective]:NullInjectorError:没有TrimInputDirective的提供者!错误:StaticInjectorError(DynamicTestModule)[TrimInputDirective]:

我不明白为什么我得到这个错误,为什么我必须提供指令?我不认为这是必要的,如果我必须提供我提供的内容吗?提供实际指令不起作用/解决错误?我很迷茫 . 如果有人能告诉我如何解决问题或为什么我得到它,我将非常感激 .

请注意,这是一个传统的Angular应用程序,是在AngularCLI可用之前构建的 . 所以它有点不正统(例如它不使用Jasmin) .

2 回答

  • 1

    1)您不需要提供您的指令,您只需要在 TestingModule 中声明它 . 然后,它将在具有相应选择器的模板中使用 .

    2)您的选择器与输入中使用的选择器不对应 . 如果要将其应用于某些类型的所有输入或更改测试,请删除 formControlName .

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

    3)指令在某些事件中被触发 . 您需要模拟这些事件才能看到效果 . 看看这个简化的例子 . (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();
      }
    
    }
    

    而且测试:

    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'); 
      });
    });
    
  • 0

    您必须在 TestComponent 中提供类似的指令

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

相关问题