尝试监视在 ngOnInit 内部调用的服务方法时未达到 Jasmine spy callFake

我正在尝试为使用服务执行 HTTP 请求以从数据库中检索信息的组件设置一些单元测试。我对角度和单元测试非常陌生,所以请耐心等待。在我的测试中,我试图监视名为的函数getClients(该函数本质上是实际执行 HTTP 请求的服务的处理程序)并使用callFake.

我遇到的问题是该getClients功能没有被覆盖,这使我相信间谍不起作用或没有监视我认为的内容。我可以说它没有被调用,因为失败消息引用了真实getClients函数中的某些内容。

测试代码

我的理解是,因为我试图监视的函数在ngOnInit函数中,所以我必须先定义间谍,然后实例化组件。我也试过在里面运行间谍it,但也没有用。

describe('handleID', () => {

    beforeEach(waitForAsync (() => {

        spyOn(service, 'getClients').and.callFake(() => {
            let companyList = [
                {
                    COMPANYDESCRIPTOR: "Hello World Inc.",
                    COMPANYNAME: "Hello World Inc.",
                    CUSTOMERID: "abcdef123456",
                    CUSTOMERKEY: 123456
                }
            ]
            
            component.companySearchService.companies.next(companyList);
            return companyList;
        });

        fixture = TestBed.createComponent(CompanySearchComponent);
        component = fixture.componentInstance;
        service = component.companySearchService;
        fixture.detectChanges();
    }));

    it("should update component.companyForm.controls['selectedCompany'] to 'Hello World Inc.'", () => {
        component.companyForm = component._formBuilder.group({
            selectedCompany: ['']
        })
        

        component.pathToNameProp = 'COMPANYNAME';
        component.pathToIdProp = ['CUSTOMERID', 'CUSTOMERKEY'];

        let id = 123456;

        component.handleID(id);

        expect(component.companyForm.get('selectedCompany')).toBe('Hello World Inc.');
    })

})

实际功能

为了清楚起见,我提供了getClients以下功能。dbService是进行 API 调用的数据库服务。makeAnApiCall返回一个observable并且subscribe只是将数据传递给另一个处理程序,该处理程序根据source.

getClients(endpoint, method, options = []) {
    this.loading.next(true);
    this.dbService
        .makeAnApiCall(endpoint, method, options)
        .subscribe(
            res => this.generateCompanyList(res, this.source.getValue())
        )
}

失败消息

失败消息引用了从数据库服务的makeAnApiCall方法返回的可观察订阅。这让我相信间谍根本不是被创造出来的,或者完全是在监视其他东西。

Failed: Cannot read properties of undefined (reading 'subscribe')
    at CompanySearchService.getClients (http://localhost:9876/_karma_webpack_/main.js:6343:13)
    at CompanySearchComponent.ngOnInit (http://localhost:9876/_karma_webpack_/webpack:/src/app/utilities/company-search/company-search.component.ts:98:39)
    ...

问题

  1. 为什么间谍不起作用?
  2. 关于单元测试,在处理不需要完全避免它们的可观察对象、承诺和 HTTP 请求时,是否有更好的方法来编写单元测试?

提前感谢您的任何帮助!

这个解决方案解决了我的问题,但它仍然没有真正回答我的问题。因此,如果有人可以对此有所了解,我很乐意将他们的回答标记为已接受的答案。

单元测试

它看起来像我调用spyOn,的顺序fixture.detectChanges,并且component.ngOnInit弄乱了服务,因此弄乱了我的Cannot read properties of undefined错误。下面更新的代码是完整的单元测试。我创建了__beforeEach这样我就不必重复嵌套单元测试中的所有内容。我最终也完全取消了调用component.ngOnInit(),因为它似乎detectChanges也同样有效。

describe('CompanySearchComponent', () => {

    let fixture, component, service;

    let __beforeEach = () => {
        fixture = TestBed.createComponent(CompanySearchComponent);
        component = fixture.componentInstance;
        service = component.companySearchService;
        
        component.companySource = 'database';
        spyOn(service, 'getClients').and.callFake(() => {
            let response = { data: { rows:[
                        {
                            COMPANYDESCRIPTOR: "Hello World Inc.",
                            COMPANYNAME: "Hello World Inc.",
                            CUSTOMERID: "abcdef123456",
                            CUSTOMERKEY: 123456
                        }
                    ]}}
             component.companySearchService.generateCompanyList(response, 'database');
        });

        fixture.detectChanges();
    }

    beforeAll(waitForAsync(__beforeEach));

    it ('should initialize the component and the service', () => {
        expect(component).toBeDefined();
        expect(service).toBeDefined();
    })

    it ('should initalize pathToNameProp to \'COMPANYNAME\' and pathToProp to [\'CUSTOMERID\', \'CUSTOMERKEY\'] with source set to \'database\'', () => {
        expect(component.pathToNameProp).toBe('COMPANYNAME');
        expect(component.pathToIdProp).toEqual(['CUSTOMERID', 'CUSTOMERKEY']);
    })

    it ('should update companies array', () => {
        expect(component.companies).toEqual([
            {
                COMPANYDESCRIPTOR: "Hello World Inc.",
                COMPANYNAME: "Hello World Inc.",
                CUSTOMERID: "abcdef123456",
                CUSTOMERKEY: 123456
            }
        ]);
    })

    describe('handleID', () => {
        
        beforeAll(waitForAsync(__beforeEach));

        it("should update selectedCompany form'", () => {
            let id = '123456';
            component.handleID(id);
            expect(component.companyForm.controls['selectedCompany'].value.COMPANYNAME).toBe('Hello World Inc.');
        })
    })
})

设置源

它可能不相关,但我想提出另一个我遇到的问题。该组件不是独立的,其他组件将其作为依赖项导入。也就是说,必须显式定义companySearchComponent.companySource然后重新初始化组件,因为它的值是在构造函数中定义的。

constructor(
    private elem: ElementRef,
    public companySearchService: CompanySearchService,
    private hierarchyService: HierarchyService, 
    private _formBuilder: FormBuilder
) {
    this.companySource = this.elem.nativeElement.dataset.companySrc;
    this.prevCompanySrc = this.companySearchService.source.getValue();
}

构造函数引用源的选择器元素

<company-search [hidden]="!showSearch" id="company-search" data-company-src="database"></company-search>

companySearchComponent.ngOnInit该源值内部,用于定义 http 请求和响应的一些重要属性。它也用于初始调用companySearchService.getClients()(我最初遇到问题的函数)。

ngOnInit() {

    switch(this.companySource) {
        case 'database':
            ...
            this.pathToNameProp = 'COMPANYNAME';
            this.pathToIdProp = ['CUSTOMERID', 'CUSTOMERKEY'];
            break;
        ...
        default:
            break;
    }

    if (!this.companySearchService.companies.value || this.prevCompanySrc !== this.companySource) {
        this.companySearchService.source.next(this.companySource);
        this.companySearchService.getClients(this.endpoint, this.httpMethod, this.httpOptions);
    }

    ...
}

就像我说的那样,这并不能完全回答我提出的问题,但它是解决问题的方法,所以如果有人发布更全面和彻底的解决方案,我很乐意将其标记为已接受的答案。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

Jasmine + TypeScript,无法找到Spy类的withArgs()方法

尝试对Array.prototype方法进行间谍监视(Jasmine)会导致堆栈溢出

使用callFake()测试HttpClient调用

尝试在ngOnInit中订阅函数时出错

监视服务方法时,Jasmine单元测试失败

我可以使用Jasmine spy编写AngularJS服务的测试用例吗?

如何监视服务以进行测试-AngularJS / Jasmine

在 ngOnInit 中调用时,Jasmine 如何使用 FormGroup 超级构造函数?

Angular / Jasmine-如果在ngOnInit上调用,间谍程序是否起作用?

如何使用Jasmine监视静态类方法

Jasmine Spy根据参数返回不同的值

Angular单元测试Jasmine Spy错误

Jestjs中spyOn()。and.callfake的替代方法

如何监视在Jasmine beforeEach代码块内部创建的函数?

Angular,Jasmine-监视函数,在$ interval / $ timeout服务内执行

使用Jasmine监视Backbone View回调方法

Angular-使用Jasmine监视HttpClient中的链接方法

这是在Jasmine中监视功能的正确方法吗?

如何使用Jasmine监视控制器方法?

使用 jasmine spy 返回 Subscribable 的组件集成测试

Jasmine Spy在Typescript类的Get()或Set()函数上

如何在Jasmine和Angular中测试ngOnInit的代码逻辑

等待ngOnInit在Jasmine Angular2中完成

无法在 jasmine 测试用例中覆盖 ngOnInit()

使用Jasmine监视构造函数

服务方法如何在ngOnInit()甚至ngOnInit()挂钩执行一次的内部工作?

回来时未调用ngOnInit

强制调用ngOnDestroy和ngOnInit

在 ngOnInit 上模拟 Observable 服务