【问题标题】:Error: <toHaveBeenCalled> : Expected a spy, but got Function in Jamsine错误:<toHaveBeenCalled>:预期是间谍,但在 Jasmine 中获得了函数
【发布时间】:2020-06-01 03:42:30
【问题描述】:

我是使用 Jasmine 和 Karma 在 Angular 中进行单元测试的新手。我已经写了一个规范文件,但我得到了一个错误。让我先展示一下我到目前为止所做的事情。我创建了一个自定义组件:

时间选择器组件

startRange = moment('12-01-01');
endRange = moment ('12-12-01');

modes= ['Calendar Year', 'Year-to-date', 'Rolling Year', 'Custom'];

date = new Date();
currentYear = this.date.getFullYear(); // or simply currentYear = 2020;

@ViewChild('primaryMonthPicker') primaryMonthPicker: MonthpickerComponent; // a child component

primaryDropDown(startRange, endRange) {
    if (this.primaryMode === this.modes[0]) {
        this.initCalendarYear(this.primaryMonthPicker, this.currentYear);
    } else if (this.primaryMode === this.modes[1]) {
        this.initYearToDate(this.primaryMonthPicker, this.currentYear);
    } else if (this.primaryMode === this.modes[2]) {
        this.initRollingYear(this.primaryMonthPicker, this.currentYear);
    }
}

在上面的代码中MonthpickerComponent是另一个自定义组件,它是TimeselectorComponent的唯一子组件。

我只想检查何时调用primaryDropDown,然后还应该调用initCalendarYear。这是规范文件:

import { TestBed, ComponentFixture, async } from '@angular/core/testing';
import { TimeselectorComponent } from './timeselector.component';
...

describe('TimeselectorComponent', () => {
    let fixture: ComponentFixture<TimeselectorComponent>;

    @Component({
        selector: 'app-monthpicker',
        template: '<div></div>'
    })
    class FakeMonthpickerComponent {
        // methods of MonthpickerComponent
    }

    let MODES = [];

    beforeEach(async(() => {
        MODES = ['Calendar Year', 'Year-to-date', 'Rolling Year', 'Custom'];
        TestBed.configureTestingModule({
            declarations: [FakeMonthpickerComponent, TimeselectorComponent],
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(TimeselectorComponent);
    });

    // other test cases that are passing

    // need help with this test case
    it('should call initCalendarYear when primaryDropDown is called', () => {
      const startRange=moment('2020-01-01');
      const endRange= moment('2020-12-01');
      spyOn(fixture.componentInstance, 'initCalendarYear');
      fixture.componentInstance.primaryMode=MODES[0];
      fixture.componentInstance.primaryDropDown(startRange, endRange);
     expect(fixture.componentInstance.initCalendarYear).toHaveBeenCalled();
  });
});

但是我收到了这个错误:

我必须测试关联的if-else 语句是否调用了正确的方法。请帮帮我。

这是stackblitz

【问题讨论】:

    标签: angular unit-testing karma-jasmine


    【解决方案1】:
    import { TestBed, ComponentFixture, async } from '@angular/core/testing';
    import { TimeselectorComponent } from './timeselector.component';
    import { Component, NO_ERRORS_SCHEMA } from '@angular/core';
    var moment = require('moment/moment')
    
    describe('TimeselectorComponent', () => {
        let fixture: ComponentFixture<TimeselectorComponent>;
    
        @Component({
            selector: 'app-monthpicker',
            template: '<div></div>'
        })
        class FakeMonthpickerComponent {
            // methods of MonthpickerComponent
        }
    
        let MODES = [];
    
        beforeEach(async(() => {
            MODES = ['Calendar Year', 'Year-to-date', 'Rolling Year', 'Custom'];
            TestBed.configureTestingModule({
                declarations: [FakeMonthpickerComponent, TimeselectorComponent],
                schemas: [NO_ERRORS_SCHEMA]
            }).compileComponents();
        }));
    
        beforeEach(() => {
            fixture = TestBed.createComponent(TimeselectorComponent);
        });
    
        // other test cases that are passing
    
        // need help with this test case
        it('should call initCalendarYear when primaryDropDown is called', () => {
          const startRange=moment('2020-01-01');
          const endRange= moment('2020-12-01');
          spyOn(fixture.componentInstance, 'initCalendarYear');
          fixture.componentInstance.primaryMode=MODES[0];
          fixture.componentInstance.primaryDropDown(startRange, endRange);
         expect(fixture.componentInstance.initCalendarYear).toHaveBeenCalled();
      });
    });
    

    上面的游戏我成功了:-

    【讨论】:

    • 但是没有我想模拟的返回值。我试着简单地传递true。那没有用。还有在expect(... 声明中我应该传递什么(????, 2020)
    • 您能否给出更具体的答案。这将非常有帮助。提前致谢。
    • 其实一般会创建一个mock对象,当你在测试一个组件并且你不想使用服务的真正实现而你只想在组件调用一个服务方法时,真正的实现应该是替换为 mock 并且应该返回一个值而不执行任何逻辑。 spyon 有助于创建一个模拟。像 toHaveBeenCalled 这样的方法只能让我检查模拟/间谍对象。
    • 嗨。我还有其他一些问题。因为我们在同一个时区。我们可以打个 Skype 电话吗?
    • @AakashGarg。我有一些新的要求。我已经编辑了这个问题。请您再检查一次。
    【解决方案2】:

    添加这样的间谍

    spyOn(fixture.componentInstance, 'initCalendarYear');
    

    用这个改变最后一行

    expect(fixture.componentInstance.initCalendarYear).toHaveBeenCalled();
    

    所以你的最终测试用例应该是这样的

    it('should call initCalendarYear when primaryDropDown is called', () => {
            const startRange=moment('2020-01-01');
            const endRange= moment('2020-12-01');
            fixture.componentInstance.primaryMode = MODES[0];
            spyOn(fixture.componentInstance, 'initCalendarYear');
            fixture.componentInstance.primaryDropDown(startRange, endRange);
    
      expect(fixture.componentInstance.initCalendarYear).toHaveBeenCalled(); 
     });
    

    【讨论】:

    • 这是完美的。但我的要求有一个小的变化。我想给你看。现在可以在Skype上使用吗?
    • 不能加入Skype,如果你能在这里解释就太好了。
    • 是的。给我一点时间,我正在尝试创建一个 stackblitz 或类似的东西。
    • 创建 stackblitz 花费的时间比预期的要长。所以我编辑了问题本身。我想分别为每个 if-else 块编写不同的测试用例。
    • 但是没有。请参阅我正在调用 3 种不同的方法,initCalendarYearinitYearToDateinitRollingYear。虽然这三个参数都是一样的,但是业务逻辑是不同的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    相关资源
    最近更新 更多