【问题标题】:How to vary spy results between each tests如何在每次测试之间改变间谍结果
【发布时间】:2020-03-18 14:09:26
【问题描述】:

我正在为依赖于服务的 Angular 组件编写一些测试。

此服务有一个stageData 公共成员,我想根据stageData 的内容测试该组件的行为是否正确。

因为我需要模拟服务,所以我创建了一个间谍并定义了它会为 stageData 返回的内容,但我无法在每个测试中更改它。

describe('ProgressComponent', () => {
  let component: ProgressComponent;
  let fixture: ComponentFixture<ProgressComponent>;
  let progressStageService: any;
  const signStage = new StageCompletionModel();
  const inkStage = new StageCompletionModel();

  beforeEach(async(() => {
    signStage.stage = Stage.Sign;
    inkStage.stage = Stage.Ink;

    progressStageService = jasmine.createSpyObj('ProgressStageService', ['getStageProgress']);
    progressStageService.stageData = [signStage];

    TestBed.configureTestingModule({
      declarations: [ ProgressComponent ],
      providers: [ { provide: ProgressStageService, useValue: progressStageService } ],
      schemas: [ NO_ERRORS_SCHEMA ],
    })
    .compileComponents();
  }));

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

  it('should not display Ink stage when it is not in stageData', () => {
    const callToAction: HTMLElement = fixture.nativeElement;
    const stageLabels = callToAction.querySelectorAll('span.stage-label');
    expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeFalsy();
  });

  it('should display Ink stage when it is in stageData', () => {
    const callToAction: HTMLElement = fixture.nativeElement;
    const stageLabels = callToAction.querySelectorAll('span.stage-label');

    progressStageService.stageData = [signStage, inkStage];
    fixture.detectChanges();

    expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeTruthy();
  });
});

我在第二个测试用例中尝试了许多变体,但 stageData 始终包含在 beforeEach 中设置的数据。

【问题讨论】:

    标签: javascript angular unit-testing jasmine


    【解决方案1】:

    因此,在模拟数据时,您必须将其拆分为更多 describes。第二个beforeEach 中的第一个fixture.detectChanges 是“捆绑”组件并调用ngOnInit

    试试:

    describe('ProgressComponent', () => {
      let component: ProgressComponent;
      let fixture: ComponentFixture<ProgressComponent>;
      let progressStageService: any;
      const signStage = new StageCompletionModel();
      const inkStage = new StageCompletionModel();
    
      beforeEach(async(() => {
        signStage.stage = Stage.Sign;
        inkStage.stage = Stage.Ink;
    
        progressStageService = jasmine.createSpyObj('ProgressStageService', ['getStageProgress']);
    
        TestBed.configureTestingModule({
          declarations: [ ProgressComponent ],
          providers: [ { provide: ProgressStageService, useValue: progressStageService } ],
          schemas: [ NO_ERRORS_SCHEMA ],
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        console.log('getting a handle on the fixture');
        fixture = TestBed.createComponent(ProgressComponent);
        console.log(fixture);
        component = fixture.componentInstance;
        // re-assign progressStageService to what was injected in the configureTestingModule
        // If you're using Angular 9, this should be .inject and not .get
        progressStageService = TestBed.get(ProgressStageService);
        // fixture.detectChanges(); => Remove fixture.detectChanges here
      });
    
     describe('stageData is [signStage]', () => {
       beforeEach(() => {
          // assign stageData to the service that was injected
          progressStageService.stageData = [signStage];
          fixture.detectChanges();
       });
    
      it('should not display Ink stage when it is not in stageData', () => {
        const callToAction: HTMLElement = fixture.nativeElement;
        const stageLabels = callToAction.querySelectorAll('span.stage-label');
        expect(Array.from(stageLabels).some(sl => sl.textContent.includes('Ink'))).toBeFalsy();
      });
     });
    
     describe('stageData is [signStage, inkStage]', () => {
       beforeEach(() => {
          // assign stageData to the service that was injected
          progressStageService.stageData = [signStage, inkStage];
          fixture.detectChanges();
       });
    
       it('should display Ink stage when it is in stageData', () => {
        const callToAction: HTMLElement = fixture.nativeElement;
        const stageLabels = callToAction.querySelectorAll('span.stage-label');
        expect(Array.from(stageLabels).some(sl => 
    sl.textContent.includes('Ink'))).toBeTruthy();
      });
     });
    });
    
    

    【讨论】:

    • 我收到一个错误,即 stageData 在 ProgressComponent 中未定义,而该夹具在我的测试中未定义。
    • 在处理 fixture 之前,我添加了两个 console.logs。确保它们在执行测试时运行。至于stageDataProgressComponent 中未定义,我虽然stageData 住在ProgressStageService 中?
    • 对不起,我的意思是说,stageDataProgressComponent 中从ProgressStageService 使用时是未定义的。我添加了两个 console.log 语句。第一个正在工作,但第二个没有被调用,即使我在语句中放了一个字符串。我也在控制台zone-evergreen.js:2857 GET ng:///ProgressComponent/%C9%B5fac.js net::ERR_UNKNOWN_URL_SCHEME 中收到此错误。目前正在调查。
    • 我可以通过在第一个 beforeEach 中设置 progressStageService.stageData = []; 来消除错误,但随后我又回到了它不更新的时候。我想我可能只写两个单独的describes。它很难看,但我无法让它工作。
    • 第二个没有被调用的方式很奇怪,但是是的,尝试两个单独的描述。我提出的“应该”是两个单独的描述,但这是用更少的代码。
    猜你喜欢
    • 2020-05-26
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    • 2012-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多