Angular 单元测试组件的@Input。变化值检测

问题描述 投票:0回答:1

我有一个简单的场景,其中(角度17.3)我试图测试输入属性更改时组件的反应。 我有一个从父组件传递的属性,它在子组件中作为 @Input 出现。类似这样的东西:

家长 ...

title = 'Hola!'
...

父级标记:

...
<my-child-component [input_title]="title" />
...

在孩子身上:

....
@Input({ required: true }) input_title: String = '';
..
...
  ngOnChanges(changes: SimpleChanges): void {
    const simpleChange = changes['input_title'];
    if (simpleChange.previousValue && (simpleChange.currentValue !== simpleChange.previousValue)) { 
      this.init(); // I WANT to test this method
    }
  }
...

所以基本上,我想测试这个方法并触发更改。此外,我想测试标题的更改。我怎样才能从单元测试中做到这一点?

我有类似的东西:

  let component: SimpleComponent;
  let fixture: ComponentFixture<SimpleComponent>;
  let compiled: HTMLElement;

  beforeEach(() => {
    fixture = TestBed.createComponent(SimpleComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

...
  it('should change the title of the component', () => {
    const playSpy = spyOn(component, 'init');
    console.log(component?.input_title);
    const newTitle= 'blablabla';
    component.input_title= newTitle;
    fixture.detectChanges();
    compiled = fixture.nativeElement as HTMLElement;
    console.log(compiled.querySelector('h1')?.textContent);
  });

这不起作用:)

angular unit-testing testing
1个回答
0
投票

通过使用测试主机组件(这是我和 Angular 的建议策略),你可以完成这项工作;即使使用 OnPush。我对你的 init 方法可能做的事情采取了随意的做法 - 长话短说,你需要设置你的标题两次,因为这就是你编写更改的方式。

简单组件的Html

<div id="initStatus">initialized: {{initialized}}</div>

.ts 文件

import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'my-child-component',

  templateUrl: './simple.component.html',
  styleUrl: './simple.component.css'
})
export class SimpleComponent implements OnChanges {
  initialized = false;
 
  @Input({ required: true }) input_title: String = '';

  ngOnChanges(changes: SimpleChanges): void {
    const simpleChange = changes['input_title'];
    if (simpleChange.previousValue && (simpleChange.currentValue !== simpleChange.previousValue)) {
      this.init(); // I WANT to test this method
    }
  }

  private init(): void {
    this.initialized = true;
  }
}

规格文件

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { SimpleComponent } from './simple.component';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';

@Component(({
  template: `<my-child-component [input_title]="title"></my-child-component>`,
}))
class TestHostComponent {
  title = 'first title';

}

describe('SimpleComponent', () => {
  let component: TestHostComponent;
  let fixture: ComponentFixture<TestHostComponent>;

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

    fixture = TestBed.createComponent(TestHostComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should init', () => {
    const debugElement = fixture.debugElement.query(By.css('#initStatus'));
    expect(debugElement.nativeElement.textContent).toBe('initialized: false');

    component.title = 'second title'; // here is what you are missing
    fixture.detectChanges();
    expect(debugElement.nativeElement.textContent).toBe('initialized: true');
  });
});

现在,我们来讨论 ngOnChanges。它会针对每次更改而运行,这就是为什么我完全避免它以帮助防止性能问题。 这是新的 ts 文件,它执行相同的操作,但带有 setter 和 OnPush

import { ChangeDetectionStrategy, Component, Input } from '@angular/core';

@Component({
  selector: 'my-child-component',

  templateUrl: './simple.component.html',
  styleUrl: './simple.component.css',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SimpleComponent {
  currentTitle: string
  initialized = false;

  @Input({ required: true }) set input_title(value: string) {
    const changed = !!this.currentTitle;
    this.currentTitle = value;
    if (changed) {
      this.init();
    }
  }

  private init(): void {
    this.initialized = true;
  }
}


以这种方式写出来可以更明显地看出,只有在更改现有标题时才会调用 init 。

祝你好运,编码愉快!

© www.soinside.com 2019 - 2024. All rights reserved.