Javascript Jasmine测试自定义http服务

Javascript Jasmine测试自定义http服务,javascript,angularjs,testing,jasmine,Javascript,Angularjs,Testing,Jasmine,我已经为我的应用程序编写了一个http服务,我想用Jasmine进行测试,但老实说,这是我第一次编写测试,我真的不知道从哪里开始,有人能告诉我好的方向吗 我已经阅读了很多关于基本Jasmine测试的教程,我明白了,但我的问题更多的是“我应该测试什么,哪些是有用的测试”,这个http服务是我培训的一个完美例子 下面是我要测试的http服务: angular.module('acme.services.http', [ 'config' ]) .factory('http', fu

我已经为我的应用程序编写了一个http服务,我想用Jasmine进行测试,但老实说,这是我第一次编写测试,我真的不知道从哪里开始,有人能告诉我好的方向吗

我已经阅读了很多关于基本Jasmine测试的教程,我明白了,但我的问题更多的是“我应该测试什么,哪些是有用的测试”,这个http服务是我培训的一个完美例子

下面是我要测试的http服务:

angular.module('acme.services.http', [
    'config'
])

    .factory('http', function ($q, $http, $cacheFactory, API_BASEPATH) {

        // **********************************************************
        // Initialize
        // **********************************************************

        var cache = $cacheFactory('toip');

        // **********************************************************
        // Public API
        // **********************************************************

        return {
            get: get,
            post: post,
            put: put,
            update: update,
            destroy: destroy,
            flush: flush,
            resource: resource
        };

        // **********************************************************
        // Published methods
        // **********************************************************

        /**
         * Execute a GET http request.
         * @param path
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function get(path, config) {
            return call('GET', path, null, config).then(extract);
        }

        /**
         * Execute a POST http request.
         * @param path
         * @param data
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function post(path, data, config) {
            return call('POST', path, data, config).then(extract);
        }

        /**
         * Execute a PUT http request.
         * @param path
         * @param data
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function put(path, data, config) {
            return call('PUT', path, data, config).then(extract);
        }

        /**
         * Execute an UPDATE http request.
         * @param path
         * @param data
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function update(path, data, config) {
            return call('PUT', path, data, config).then(extract);
        }

        /**
         * Execute a DESTROY http request
         * @param path
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function destroy(path, config) {
            return call('DELETE', path, null, config).then(extract);
        }

        /**
         *
         * @param path
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function resource(path, config) {
            var ext = config.hasOwnProperty('ext') ? '.' + config.ext : '';
            return $http.get(toUrl(path) + ext, config).then(function (result) {
                return result.data;
            });
        }

        /**
         * Flush the HTTP cache.
         * @param {array} keys
         * @returns {*}
         */
        function flush() {
            var keys = angular.isArray(arguments[0]) ? arguments[0] : arguments;

            if (!keys.length) {
                return cache.removeAll();
            }
            angular.forEach(keys, function (path) {
                cache.remove(toUrl(path));
            });
        }

        // **********************************************************
        // Internal methods
        // **********************************************************

        /**
         * Execute an HTTP request and parse the result.
         * @param method
         * @param path
         * @param data
         * @param config
         * @returns {qFactory.Deferred.promise|*}
         */
        function call(method, path, data, config) {
            config = config || {};

            var defer = $q.defer();
            var url = toUrl(path);
            var handleData = (-1 !== ['PUT', 'POST'].indexOf(method));
            var dontCache;

            // don't cache if {cache: false} or if method != GET
            if (('undefined' !== typeof config && config.hasOwnProperty('cache') && false === config['cache']) ||
                (-1 == ['GET'].indexOf(method))) {
                dontCache = true;
            }

            // try to get data from cache
            var result = cache.get(url);

            if (!result || dontCache) {
                // add method & url
                config = angular.extend({
                    method: method,
                    url: url
                }, config);

                // force false here as we'll cache later
                config.cache = false;

                if (handleData && 'undefined' !== typeof data) {
                    config.data = data;
                }

                $http(config)
                    .success(function (result) {
                        if (!dontCache) {
                            cache.put(url, result);
                        }
                        defer.resolve(result);
                    })
                    .error(function (result) {
                        defer.reject(result['errors']);
                    });

            } else {
                defer.resolve(result);
            }

            // if method invoked were PUT/POST or DELETE, remove all cache
            if (-1 !== ['PUT', 'POST', 'DELETE'].indexOf(method)) {
                flush();
            }

            return defer.promise;
        }

        /**
         * Convert a dot notation path to an url.
         * @param path
         * @returns {string}
         */
        function toUrl(path) {
            if ('local:' === path.substring(0, 6)) {
                path = path.substring(6);
                return path.split('.').join('/');
            }

            return API_BASEPATH + '/' + path.split('.').join('/');
        }

        /**
         * Extract the HTTP request response.
         * @param response
         * @returns {*}
         */
        function extract(response) {
            if (false === response.success) {
                return $q.reject(response['errors'][0]);
            }
            return response['payload'];
        }
    });
我会检查Angular的:

伪HTTP后端实现,适用于使用$HTTP服务的单元测试应用程序

我会检查Angular的:

伪HTTP后端实现,适用于使用$HTTP服务的单元测试应用程序


编写单元测试是关于验证每个单元(在本例中为函数)是否完全执行其预期的操作。但是我相信你会在互联网上找到很多资源来解释单元测试背后的想法。但首先,我可以分享一些我在编写测试时的想法

以toURL函数为例,您必须问自己:“如果
path
为[…],该函数应该做什么?”

这应包括演示功能如何工作的案例,以及可能破坏功能的案例。(我觉得如果你总是想办法破解你的代码,这对成为一个更好的程序员真的很有帮助)

例如:如果使用
null
调用函数,会发生什么情况

如果用空字符串、数字或“local:”调用它,会发生什么

对于可以想到的每一个可能的路径值,您不必为函数编写100个测试。您应该尝试找出哪些情况是真实的场景(如果其他人而不是您正在使用您的功能,非常模糊的事情会变得真实;)

试着编写测试而不看你的函数,只考虑场景和你想要发生的事情

为了测试进行http调用的函数,您需要模拟调用(假设您不希望每次运行测试时都对服务器进行实际调用)

我希望你能从中得到一些帮助


有趣的测试:)

编写单元测试是为了验证每个单元(在本例中是函数)是否完全按照预期的方式工作。但是我相信你会在互联网上找到很多资源来解释单元测试背后的想法。但首先,我可以分享一些我在编写测试时的想法

以toURL函数为例,您必须问自己:“如果
path
为[…],该函数应该做什么?”

这应包括演示功能如何工作的案例,以及可能破坏功能的案例。(我觉得如果你总是想办法破解你的代码,这对成为一个更好的程序员真的很有帮助)

例如:如果使用
null
调用函数,会发生什么情况

如果用空字符串、数字或“local:”调用它,会发生什么

对于可以想到的每一个可能的路径值,您不必为函数编写100个测试。您应该尝试找出哪些情况是真实的场景(如果其他人而不是您正在使用您的功能,非常模糊的事情会变得真实;)

试着编写测试而不看你的函数,只考虑场景和你想要发生的事情

为了测试进行http调用的函数,您需要模拟调用(假设您不希望每次运行测试时都对服务器进行实际调用)

我希望你能从中得到一些帮助

享受测试的乐趣:)