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

Posted

技术标签:

【中文标题】角度测试错误 - NullInjectorError:没有 TrimInputDirective 的提供者【英文标题】:Angular Test Error - NullInjectorError: No provider for TrimInputDirective 【发布时间】:2018-11-11 08:50:10 【问题描述】:

我创建了一个 Angular 指令,它使用 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
  );
);

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

HeadlessChrome 0.0.0 (Mac OS X 10.12.6) Trim Directive “before each” “应该修剪输入”的钩子失败未捕获(承诺):错误: StaticInjectorError(DynamicTestModule)[TrimInputDirective]: StaticInjectorError(平台:核心)[TrimInputDirective]: NullInjectorError:没有 TrimInputDirective 的提供者!错误:StaticInjectorError(DynamicTestModule)[TrimInputDirective]:

我不明白为什么会出现这个错误,为什么我必须提供指令?我不认为这是必要的,如果我必须提供我应该提供什么?提供实际指令不起作用/解决错误?我很迷茫。如果有人能告诉我如何解决这个问题或为什么我会得到它,我将不胜感激。

请注意,这是一个旧版 Angular 应用程序,并且是在 AngularCLI 可用。所以它有点不正统(例如它没有使用 Jasmin)。

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

我试过了,然后我被要求提供更多的供应商,这自然是我尝试的第一件事。我认为我的实现有问题 指令应声明为未提供。 angular.io/guide/…【参考方案2】:

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

【讨论】:

以上是关于角度测试错误 - NullInjectorError:没有 TrimInputDirective 的提供者的主要内容,如果未能解决你的问题,请参考以下文章

在开玩笑的单元测试角度显示正确的错误

获取错误:在角度 js 中进行单元测试时出现 $injector:modulerr 模块错误

如何订阅角度服务单元测试中的错误案例

正则表达式测试作为属性的指令实现错误 - 验证(打字稿+角度)

使用 Jasmine 和 Karma 进行角度单元测试时出错

角度测试 - 如何在角度测试中模拟 document.getElementbyId?