我有一个简单的场景,其中(Angular 17.3)我要测试的是组件在输入属性更改时的react . 我有一个从父组件传递的属性,它在子组件中作为@Input出现.大概是这样的:

parent. 这是... 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);
  });

这不起作用:)

推荐答案

通过使用测试主机组件(which is my and Angular's suggested strategy),您可以实现这一点;即使使用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');
  });
});

Now, let's discuss ngOnChanges. It runs for EVERY CHANGE, which is why I avoid it entirely to help prevent performance issues. Here is the new TS文件文件 that does the same thing but with a setter and 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只有在你改变一个现有的标题时才会被调用.

祝你好运,编程快乐!

Angular相关问答推荐

Angular - Bootstrap 5 Carbon不工作

FormArray包含嵌套的FormGroups.如何访问嵌套的表单组控件?

接收错误NullInjectorError:R3InjectorError(_AppModule)[config—config]....>过度安装Angular—Markdown—Editor

导致无限请求的Angular HttpInterceptor和HttpClientModule

身份验证卫士给了我17个Angular 的任何类型

代码优化以删除Angular中的嵌套订阅

在Angular 为17的独立零部件中使用@NGX-平移

我正在try 将一个带有模块联盟的Angular 8项目expose 到另一个Angular 项目,Angular 是15,这可能吗?如果是这样,有什么建议吗?

Primeng:安装ANGLE 16.2.0版本

如何使用来自不同可观测对象的值更新可观测对象

ngrx 效果等到从初始值以外的其他状态收到响应

显示带有数组子列的标题表 - Angular

执行ng generate environments时出错时需要合集和原理图

res.json() 不是函数

如何在 Angular 2 中获取与 ElementRef 关联的组件的引用

material Angular手风琴header/title高度

如何设置背景 colored颜色 IONIC 4

如何在 Angular 2 的同一个组件中使用多个 ng-content?

如何在 Angular 2 中跟踪路由?

IE11中的Angular4应用程序运行问题