【问题标题】:Angular 2 Component variable not updated in html template by my test我的测试未在 html 模板中更新 Angular 2 组件变量
【发布时间】:2016-12-03 21:47:35
【问题描述】:

我正在尝试使用 输入外部模板 测试 angular 2 组件

这是组件:

@Component({
  selector: 'text-counter',
  templateUrl: 'text-counter.component.html',
  styleUrls: ['text-counter.component.css']
})
export class TextCounterComponent implements OnChanges {

  @Input() inputText: string;
  @Input() min: number;
  @Input() max: number;
  @Input() tolerance: number = 20;

  message: string;
  messageClass: string;

  constructor() {
  }

  ngOnChanges(changes: SimpleChanges) {

    const input: string = changes['inputText'].currentValue;

    function expandMessage(rawMessage, n) {
      return rawMessage.replace('%', n);
    }

    const settings = {
      initialPrompt: 'saisissez au moins % caractères',
      nToGoPrompt: 'il manque % caractères',
      nLeftPrompt: 'encore % caractères autorisés',
      tooLongByPrompt: 'trop long de % caractères'
    };

    const length = input ? input.length : 0;

    this.messageClass = 'lightblue';

    if (length === 0) {
      this.message = expandMessage(settings.initialPrompt, this.min);
    }
    else if (length > 0 && length < this.min) {
      this.message = expandMessage(settings.nToGoPrompt, this.min - length);
    }
    else if (length >= this.min && length <= this.max) {
      if (length > this.max - this.tolerance) {
        this.messageClass = 'Gold';
      }
      this.message = expandMessage(settings.nLeftPrompt, this.max - length);
    }
    else {
      this.messageClass = 'Red';
      this.message = expandMessage(settings.tooLongByPrompt, length - this.max);
    }
  }
}

及其模板:

<span class="form-text" [ngClass]="messageClass">
  {{message}}
</span>

尽管ngOnChanges 实际上被调用...

这里是测试:

@Component({
  selector: `test-host-component`,
  template: `<div> 
           <text-counter
              [inputText]="valueFromHost"
              [min]="2"
              [max]="500">
            </text-counter>
            </div>`
})
export class TestHostComponent {
  /* using viewChild we get access to the TestComponent which is a child of TestHostComponent */
  @ViewChild(TextCounterComponent)
  public textCounterComponent: any;
  /* this is the variable which is passed as input to the TestComponent */
  public valueFromHost: string;
}

describe('Component: TextCounter', () => {

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TextCounterComponent, TestHostComponent]
    })
      .compileComponents();
  }));

  it('should indicate min message', () => {
    const hostComponentFixture = TestBed.createComponent(TestHostComponent);
    const textCounterComponentFixture = TestBed.createComponent(TextCounterComponent);

    const de: DebugElement = textCounterComponentFixture.debugElement.query(By.css('span'));
    const el: HTMLElement = de.nativeElement;
    let testHostComponent = hostComponentFixture.componentInstance;

    testHostComponent.valueFromHost = 'a';

    spyOn(testHostComponent.textCounterComponent, 'ngOnChanges').and.callThrough();
    hostComponentFixture.detectChanges();
    textCounterComponentFixture.detectChanges();
    expect(testHostComponent.textCounterComponent.ngOnChanges).toHaveBeenCalled();

    expect(el.textContent).toContain('il manque');//This fails!!
  });
});

这是失败的断言:

Chrome 54.0.2840 (Mac OS X 10.12.1) Component: TextCounter should indicate min message FAILED
        Expected '

        ' to contain 'il manque'.

有人可以帮忙吗?

edit 1:我尝试通过将 el HTMLElement 的检索移动到夹具 detectChanges() 之后来更改我的代码,如下所示:

...
const de: DebugElement = textCounterComponentFixture.debugElement.query(By.css('span'));
const el: HTMLElement = de.nativeElement;

expect(el.textContent).toContain('il manque');

无济于事...

【问题讨论】:

  • 我在这里唯一的见解是您在fixture.detectChanges() 之前得到el。尝试在fixture.detectChanges 之后获取el

标签: angular angular2-template


【解决方案1】:

当您调用TestBed.createComponent 方法时,之前的组件会从 DOM 中移除。

第一步

TestBad.createComponent

...
const rootElId = `root${_nextRootElementId++}`;
testComponentRenderer.insertRootElement(rootElId); <== see this line

https://github.com/angular/angular/blob/2.2.4/modules/%40angular/core/testing/test_bed.ts#L358

第二步

打开@angular/platform-b​​rowser-dynamic/testing/dom_test_component_renderer.ts

insertRootElement() {
  ...
  // TODO(juliemr): can/should this be optional?
  const oldRoots = getDOM().querySelectorAll(this._doc, '[id^=root]');
  for (let i = 0; i < oldRoots.length; i++) {
    getDOM().remove(oldRoots[i]);
  }
  ...

https://github.com/angular/angular/blob/2.2.4/modules/%40angular/platform-browser-dynamic/testing/dom_test_component_renderer.ts#L27-L30

我会这样写:

it('should indicate min message', () => {
  const hostComponentFixture = TestBed.createComponent(TestHostComponent);
  //const textCounterComponentFixture = TestBed.createComponent(TextCounterComponent);

  const de: DebugElement = hostComponentFixture.debugElement
    .query(By.css('text-counter > span'));
  const el: HTMLElement = de.nativeElement;
  let testHostComponent = hostComponentFixture.componentInstance;

  testHostComponent.valueFromHost = 'a';  
  hostComponentFixture.detectChanges();  

  expect(el.textContent).toContain('il manque');//This should work!!
});

Plunker Example

【讨论】:

  • 感谢 Yurzui!发现。出于纯粹的好奇,您能否告诉我您所描述的行为记录在哪里(之前的组件已从 DOM 中删除)?
  • 我已经添加了相关信息
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-19
  • 1970-01-01
  • 2021-06-12
相关资源
最近更新 更多