【问题标题】:How do I update component variables in Angular unit tests?如何在 Angular 单元测试中更新组件变量?
【发布时间】:2021-07-12 16:03:20
【问题描述】:

我在测试中设置“headerButtons”和“contractLoaded”组件变量时遇到问题,但它似乎没有更改值。如果我在测试运行时控制台输出或在组件中使用调试器,则变量将保持最初定义的状态(未定义和错误)。

我尝试了很多不同的组合,但结果总是一样。

headerButtons 是一个@Input

let component: HeaderBannerComponent;
  let fixture: ComponentFixture<HeaderBannerComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({ declarations: [HeaderBannerComponent] });
    fixture = TestBed.createComponent(HeaderBannerComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

...

it('should display the correct amount of header buttons', () => {
    const debugEl: DebugElement = fixture.debugElement;

    component.headerButtons = 'basic';
    fixture.detectChanges();

    expect(debugEl.queryAll(By.css('.header-banner__btn-link')).length).toEqual(
      2
    );

    component.headerButtons = 'full';
    component.contractLoaded = true;
    fixture.detectChanges();
    expect(
      debugEl.queryAll(By.css('.header-banner__btn-link')).length
    ).toBeGreaterThan(2);
  });

组件:

import { Component, Input, OnInit } from '@angular/core';
import { KeyValue } from '@angular/common';
import { ContractEventService } from 'src/app/core/services/contract-event.service';

@Component({
  selector: 'app-header-banner',
  templateUrl: './header-banner.component.html',
  styleUrls: ['./header-banner.component.css'],
})
export class HeaderBannerComponent implements OnInit {
  menubar: Map<string, string> = new Map<string, string>();
  contractLoaded: boolean = false;

  @Input() headerTitle: any;
  @Input() headerButtons: string;

  constructor(private contractEventService: ContractEventService) {}

  ngOnInit(): void {
    if (this.headerButtons === 'full') {
      this.contractEventService.getContractLoaded().subscribe(
        (rs) => {
          this.contractLoaded = rs;
          this.setButtons();
        },
        (err) => {
          console.warn('failed to get contractLoaded status', err);
        }
      );
    }
    this.setButtons();
  }

  setButtons(): void {
    this.menubar = new Map<string, string>();
    this.menubar.set('Home', '/iforis/main.do');

    if (this.headerButtons === 'full' && this.contractLoaded) {
      this.menubar.set('Contacts', '/iforis/s/contacts/');
      this.menubar.set('Notes', '/iforis/s/notes/');
    }
    if (this.headerButtons === 'full' && !this.contractLoaded) {
      this.menubar.delete('Contacts');
      this.menubar.delete('Notes');
    }

    this.menubar.set('Exit', '/iforis/exit.do');
  }

  originalOrder = (
    a: KeyValue<string, string>,
    b: KeyValue<string, string>
  ): number => {
    return 0;
  };
}

模板:

<div class="header-banner box-shadow">
  <div class="header-banner__title">
    <span id="headerTitleText" class="header-banner__text">
      {{ headerTitle }}
    </span>
  </div>

  <div class="header-banner__logo">
    <img src="assets/DAFM_Logo_2018.png" />
  </div>

  <div class="header-banner__btn-container">
    <div
      *ngFor="
        let button of menubar | keyvalue: originalOrder;
        let first = first;
        let last = last
      "
      [ngClass]="[
        'header-banner__btn',
        first ? 'header-banner__btn--first' : '',
        last ? 'header-banner__btn--last' : ''
      ]"
    >
      <a href="{{ button.value }}" class="header-banner__btn-link">
        {{ button.key }}
      </a>
    </div>
  </div>
</div>

【问题讨论】:

    标签: javascript angular unit-testing jasmine


    【解决方案1】:

    这很奇怪。你能同时展示 HTML 和组件的打字稿吗?

    我想我对您可能面临的问题有所了解。

    it('should display the correct amount of header buttons', () => {
        // get rid of this variable
        // const debugEl: DebugElement = fixture.debugElement;
    
        component.headerButtons = 'basic';
        fixture.detectChanges();
    
        // Change this line to be fixture.debugElement and not debugEl
        expect(fixture.debugElement.queryAll(By.css('.header-banner__btn-link')).length).toEqual(
          2
        );
    
        component.headerButtons = 'full';
        component.contractLoaded = true;
        fixture.detectChanges();
        // Change this line to be fixture.debugElement and not debugEl
        expect(
          fixture.debugElement.queryAll(By.css('.header-banner__btn-link')).length
        ).toBeGreaterThan(2);
      });
    

    问题是debugEl。在您更改组件变量后它会变得陈旧,因此在更改变量后您总是需要一个新的debugEl。我认为这很可能是问题所在。

    ====== 编辑 ====== 也许我们应该模拟 ContractEventService 而不是提供真实的。

    试试这个:

    let component: HeaderBannerComponent;
    let fixture: ComponentFixture<HeaderBannerComponent>;
    let mockContractEventService: jasmine.SpyObj<ContractEventService>;
    
      beforeEach(() => {
        // the first string argument is just an identifier and is optional
        // the second array of strings are public methods that we need to mock
        mockContractEventService = jasmine.createSpyObj<ContractEventService>('ContractEventService', ['getContractLoaded']);
        TestBed.configureTestingModule({ 
          declarations: [HeaderBannerComponent], 
          // provide the mock when the component asks for the real one
          providers: [{ provide: ContractEventService, useValue: mockContractService }] 
        });
        fixture = TestBed.createComponent(HeaderBannerComponent);
        component = fixture.componentInstance;
        // formatting is off but return a fake value for getContractLoaded
     mockContractEventService.getContractLoaded.and.returnValue(of(true));
        // the first fixture.detectChanges() is when ngOnInit is called
        fixture.detectChanges();
      });
    
    it('should display the correct amount of header buttons', () => {
        // get rid of this variable
        // const debugEl: DebugElement = fixture.debugElement;
    
        component.headerButtons = 'basic';
        // The issue was that we are not calling setButtons
        // manually call setButtons
        component.setButtons();
        fixture.detectChanges();
    
        // Change this line to be fixture.debugElement and not debugEl
        expect(fixture.debugElement.queryAll(By.css('.header-banner__btn-link')).length).toEqual(
          2
        );
    
        component.headerButtons = 'full';
        component.contractLoaded = true;
        // manually call setButtons
        component.setButtons();
        fixture.detectChanges();
        // Change this line to be fixture.debugElement and not debugEl
        expect(
          fixture.debugElement.queryAll(By.css('.header-banner__btn-link')).length
        ).toBeGreaterThan(2);
      });
    

    【讨论】:

    • 嘿,我已经更新了我的问题以包含组件和模板代码。我在上面尝试了您的建议,但不幸的是,它没有用。我有一项服务被注入到组件中,但我在测试中没有对此做任何事情,不知道这是否会成为问题?
    • 查看我的编辑。主要问题是我们没有手动调用setButtonsngOnInit 只运行一次,因此之后发生的任何事情,我们都必须再次手动调用 setButtons。您调用的第一个 fixture.detectChanges() 是在调用 ngOnInit 时,如果您想在调用第一个 fixture.detectChanges() 之前进行所有的模拟,这也可以工作,但它可以做更多工作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-09-22
    • 1970-01-01
    • 1970-01-01
    • 2021-01-05
    • 2020-08-23
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多