【问题标题】:How to unit test with Jasmine on multiple chained functions with returns?如何使用 Jasmine 对具有返回的多个链式函数进行单元测试?
【发布时间】:2018-07-13 19:23:01
【问题描述】:

我有以下功能:

/**
* filters array down to the given allowed keys
* @param {Object} data
* @param {String[]} allowedKeys
*/
$scope.filterData = function(data, allowedKeys) {
    return Object.keys(data)
        .filter(function(key) {
            return allowedKeys.includes(key);
        })
        .reduce(function(obj, key) {
            obj[key] = data[key];
            return obj;
        }, {});
};

我想为其创建一个单元测试,到目前为止我有以下内容:

describe('$scope.filterData', function() { 
        //params
        var data = {
            key1: "value1",
            key2: "value2",
            key3: "value3"
        }
        var allowedKeys = ["key1", "key2"];
        //mockobject
        var $Object = jasmine.createSpyObj('Object', ['keys', 'filter', 'reduce']);

        it('should func', function() {

            $Object.keys.and.returnValue($Object);
            $Object.filter.and.returnValue($Object);
            $Object.reduce.and.returnValue($Object);

            $scope.filterData(data, allowedKeys);
            expect(Object.filter).toHaveBeenCalled();
        });
    });

我遇到的问题是我收到以下错误:

TypeError: undefined is not a constructor (evalating 'allowedKeys.includes(key)')

我不明白,如何解决这个错误?

【问题讨论】:

    标签: javascript angularjs unit-testing jasmine


    【解决方案1】:

    我不知道这个解决方案是否可以帮助您
    当我们编写测试用例,尤其是函数测试时,我们倾向于测试函数的输入和输出值。
    而对我来说,我认为我们不应该通过调用 jasmine mock 来触及 Array, Object 的原始功能。
    如果您想知道在这种情况下您的功能是否正常工作。

    例如:

    describe('$scope.filterData', function() { 
        //params
        var data = {
            key1: "value1",
            key2: "value2",
            key3: "value3"
        }
        var allowedKeys = ["key1", "key2"];
    
        it('should return 2 key', function() {            
            var expected = {
              key1: "value1",
              key2: "value2",
            }
    
            var value = $scope.filterData(data, allowedKeys);
            expect(JSON.stringify(value)).toEqual(JSON.stringify());
        });
    });
    

    当然,有时我们必须模拟一些函数,比如当我们有 http 请求并且必须等待时,或者我们有来自其他地方的函数要使用并想要模拟它们。
    但是在这种情况下,您的函数对于expect 某些函数to be called 并不是真正必需的,它就足够了,不依赖于其他任何东西。
    所以最好我们只关注函数的输入和输出值

    【讨论】:

    • JSON.stringify() 中的参数不见了,应该是JSON.stringify(expected),对吧?
    • 是的,这是我的错字,应该是 JSON.stringify(expected)
    【解决方案2】:

    首先,Object 没有filter 的功能,而您的jasmine.createSpyObj 实际上是无用的。正如@Luan Phan 在他的回答中所说,我们通常倾向于测试函数的输入和输出值。 Javascript 内置函数不需要在我们的测试中进行测试。

    但是,如果您想知道,例如,如果在您的函数中调用了 Object.keys,这里有一个示例

    it('should func', function () {                        
        spyOn(Object, 'keys').and.callThrough();
    
        $scope.filterData(data, allowedKeys);                     
    
        expect(Object.keys).toHaveBeenCalled();
    });
    

    filterData 中使用的其余内置函数也可以这样做

    it('should func', function () {
        spyOn(Object, 'keys').and.callThrough();
        spyOn(Array.prototype, 'filter').and.callThrough();
        spyOn(Array.prototype, 'reduce').and.callThrough();
    
        $scope.filterData(data, allowedKeys);
    
        expect(Object.keys).toHaveBeenCalled();
        expect(Array.prototype.filter).toHaveBeenCalled();
        expect(Array.prototype.reduce).toHaveBeenCalled();
    });
    

    如果你真的需要mock其中一个内置函数返回什么,这里有一个例子

    it('should func', function () {
        const mockResult = {};
    
        spyOn(Array.prototype, 'reduce').and.returnValue(mockResult);
    
        const result = filterData(data, allowedKeys);
    
        expect(result).toBe(mockResult);
    });
    

    同样,Javascript 的内置函数已经在其他地方编写了测试,我们不需要在测试中对其进行测试,我们的重点应该放在我们编写的函数上。 p>

    希望对你有帮助

    【讨论】:

      猜你喜欢
      • 2015-03-14
      • 2021-01-29
      • 2018-11-02
      • 1970-01-01
      • 2018-06-02
      • 2016-03-29
      • 2020-09-28
      • 1970-01-01
      • 2015-10-26
      相关资源
      最近更新 更多