【问题标题】:is there way in jasmine/sinon to stub nested fucntions call?jasmine/sinon 有没有办法存根嵌套函数调用?
【发布时间】:2019-11-04 16:54:45
【问题描述】:

我正在尝试测试类,所以我在类core 中有一个方法,它正在调用另一个方法getDrugsByName。我已经编写了将调用core 的单元测试,然后它还应该调用存根方法getDrugByName。出于某种原因,下面的测试通过了,但代码覆盖率没有显示核心内覆盖的代码。知道什么是错误的实现以及如何存根嵌套方法。

DrugPriceApi.node.ts

    export class DrugPriceApi extends Types.ModuleBase<DrugPriceParam, DrugPriceResultSet[]> {
        core(args: DrugPriceParam, requestMethod: Interface.Core.RequestMethod, _dependencies: any): Promise<any> {
            this._dependencies = _dependencies;

            return new Promise<any>((resolve: Function, reject: Function) => {
                if (!_.isEmpty(args.drugName)) {
                    let drugSearchParam: any = _.cloneDeep(args);
                    drugSearchParam.searchText = args.drugName;

                    this.getDrugsByName(drugSearchParam, requestMethod).then((response: any) => {
                        if (response.header.statusCode === '0000' && _.has(response, 'detail.drugDetailsList.drug')) {
                            let drugObject = response.detail.drugDetailsList;
                            let drugFound: boolean = false;
                            drugObject.drug = Array.isArray(drugObject.drug) ? drugObject.drug : [drugObject.drug];
                            drugObject.drug.forEach((item: any) => {
                                if (!drugFound) {
                                    drugFound = ((item.mailDrug && item.mailDrug.ndcId === args.ndcId) || (item.retailDrug && item.retailDrug.ndcId === args.ndcId));
                                }
                            });

                            if (!drugFound) {
                                reject({
                                    statusDesc: "NDCID does not belong to the Drug Name provided.",
                                    statusCode: "5000",
                                    refId: response.header.refId
                                });
                            } else {
                                this.getDrugPrice(args, drugObject, requestMethod, resolve, reject, this.Handler);
                            }
                        } 
                    }).catch((_err: any) => {
                        reject({
                            statusCode: '9999',
                            refId: args.refId,
                            statusDesc: _err.statusDesc
                        });
                    });
                } 
            });
        }
    }

function getDrugsByName () {
    // return Response after making backend call 
}

DrugPriceApi.node.spec.ts

    import { } from 'jasmine';
    import { DrugPriceParam } from "./DrugPriceApi.interface";
    import { DrugPriceApi } from "./DrugPriceApi.node";
    import sinon from "sinon";

    describe("DrugPriceApi", () => {
 let stubbedHttp:any;
    const successResponse = {
        "details": {
            "drugDetailsList": {
                "drug": [{
                    "mailDrug": {
                        "ndcId": "71015523"
                    },
                    "drugForm": "Tab"
                }]
            }
        },
        "header": {
            "statusDesc": "Success",
            "statusCode": "0000"
        }
    };
    beforeEach(function() {
        // Below are the modules that are called by GetRxHistoryDetails
        // since this is a unit test, these modules should not be tested
        // these stubs will stop the real modules from executing
        stubbedHttp = sandbox.createStubInstance(HttpRequest);
        stubbedHttp.makeRequest.callsFake(function() {
            return new Promise<any>((resolve) => {
                resolve(successResponse);
            });
        });
    });
    it("should call Core", function (done) {
        let param = {
            tokenId: '123',
            appName: 'CMK_WEB',
            refId: '123'
        } as DrugPriceParam;
            param.drugName = 'ACITRETIN';
            let mockHttp: any;
            let _dependencies: any;
            let Service = DrugPriceApi.prototype;
            spyOn(Service, 'core').and.callFake(() => {
                return {
                    getDrugsByName: function() {
                        Promise.resolve(stubbedHttp);
                    }
                }
            });
            Service.core.call(param,mockHttp,_dependencies);
            expect(Service.core).toHaveBeenCalled(); 
            done();
            });
    };

【问题讨论】:

    标签: javascript unit-testing jasmine sinon


    【解决方案1】:

    您的代码覆盖率向您展示了您在测试中真正拥有的东西,老实说,您没有测试您的 core 函数,甚至没有被调用,该函数可能充满错误,您的测试甚至不会注意到它。您正在调用和测试的是在spyOn(Service, 'core').and.callFake 行中创建的存根函数,该行完全覆盖了原始core 函数。例如,您需要模拟的是getDrugsByName 函数,但是,如果它有很多业务逻辑,您也需要考虑为该函数编写测试。

    所以,像这样改变你的测试

    it("should call Core", function (done) {
        let param = {
            tokenId: '123',
            appName: 'CMK_WEB',
            refId: '123'
        } as DrugPriceParam;
            param.drugName = 'ACITRETIN';
            let mockHttp: any;
            let _dependencies: any;
            let Service = DrugPriceApi.prototype;
    
            // spyOn "core" using "callThrough" to track all calls to it but 
            // delegate to the actual implementation.
            spyOn(Service, 'core').and.callThrough();
    
            // spyOn "getDrugsByName" using "returnValue"
            spyOn(Service, 'getDrugsByName').and.returnValue(Promise.resolve(stubbedHttp));
    
            Service.core.call(param,mockHttp,_dependencies);
            expect(Service.core).toHaveBeenCalled(); 
            done();
            });
      };
    

    请注意使用spyOn(Service, 'core').and.callThrough(); 来跟踪对它的所有调用,但委托给实际实现。这些将为您提供函数的完整代码覆盖率。

    这个简单的更改将带您进入下一步。但是让我列出一些细节

    • 可能stubbedHttp 不是getDrugsByName 应该返回的。您可能需要对其进行调整。
    • mockHttpundefined 并且您将它作为参数传递给 core。注意错误。
    • _dependenciesundefined 并且您将它作为参数传递给 core。注意错误。

    希望对你有帮助

    【讨论】:

    • 谢谢你的解释很有意义,我想我明白它应该如何实施。
    猜你喜欢
    • 2016-07-11
    • 2015-02-12
    • 1970-01-01
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 2021-02-20
    • 1970-01-01
    相关资源
    最近更新 更多