【问题标题】:How do I mock the result in a $http.get promise when testing my AngularJS controller?在测试我的 AngularJS 控制器时,如何在 $http.get 承诺中模拟结果?
【发布时间】:2013-07-23 10:39:38
【问题描述】:

经过大量阅读,从 AngularJS 控制器调用 Web 服务的推荐方法似乎是使用工厂并从中返回一个承诺。

这里我有一个调用示例 API 的简单工厂。

myApp.factory('MyFactory', ['$http',function($http) {
var people = {
        requestPeople: function(x) {
            var url = 'js/test.json';
            return $http.get(url);
        }
    };
return people;
}]);

这就是我在控制器中的调用方式

myApp.controller('MyCtrl1', ['$scope', 'MyFactory', function ($scope, MyFactory) {
        MyFactory.requestPeople(22).then(function(result) {
             $scope.peopleList = result;
        });
}]);

虽然它工作正常,但我希望能够模拟在调用 then 时传入的 result。这可能吗?

到目前为止,我的尝试没有产生任何结果。这是我的尝试:

//Fake service
var mockService = {
    requestPeople: function () {
        return {
            then: function () {
                return {"one":"three"};
            }
        }

    }
};


//Some setup
beforeEach(module('myApp.controllers'));
var ctrl, scope;

beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();

    ctrl = $controller('MyCtrl1', { $scope: scope, MyFactory: mockService });
}));

//Test
it('Event Types Empty should default to false', inject(function () {
    expect(scope.peopleList.one).toBe('three');
}));

我在 karma runner 中运行时遇到的错误是

TypeError: 'undefined' 不是一个对象(评估 'scope.peopleList.one')

如何让这个测试与我的模拟数据一起工作?

【问题讨论】:

  • 我已经阅读过它,但我的控制器不需要 $http。你能详细说明你的意思吗?

标签: javascript unit-testing angularjs mocking jasmine


【解决方案1】:

我不认为 $httpBackend 是您在这里所追求的,您希望在不依赖 $http 的情况下模拟整个工厂?

查看$q,特别是测试标题下的代码示例。您的问题可能会通过如下所示的代码得到解决:

'use strict';

describe('mocking the factory response', function () {

    beforeEach(module('myApp.controllers'));

    var scope, fakeFactory, controller, q, deferred;

    //Prepare the fake factory
    beforeEach(function () {
        fakeFactory = {
            requestPeople: function () {
                deferred = q.defer();
                // Place the fake return object here
                deferred.resolve({ "one": "three" });
                return deferred.promise;
            }
        };
        spyOn(fakeFactory, 'requestPeople').andCallThrough();
    });

    //Inject fake factory into controller
    beforeEach(inject(function ($rootScope, $controller, $q) {
        scope = $rootScope.$new();
        q = $q;
        controller = $controller('MyCtrl1', { $scope: scope, MyFactory: fakeFactory });
    }));

    it('The peopleList object is not defined yet', function () {
        // Before $apply is called the promise hasn't resolved
        expect(scope.peopleList).not.toBeDefined();
    });

    it('Applying the scope causes it to be defined', function () {
        // This propagates the changes to the models
        // This happens itself when you're on a web page, but not in a unit test framework
        scope.$apply();
        expect(scope.peopleList).toBeDefined();
    });

    it('Ensure that the method was invoked', function () {
        scope.$apply();
        expect(fakeFactory.requestPeople).toHaveBeenCalled();
    });

    it('Check the value returned', function () {
        scope.$apply();
        expect(scope.peopleList).toBe({ "one": "three" });
    });
});

我已经围绕 $apply 的功能添加了一些测试,直到我开始玩这个我才知道!

哥们

【讨论】:

  • 这非常有效。我遇到了类似的情况,但我缺少的是 defer 返回正确的值,现在 $scope.apply() 更有意义。
  • 我已经成功地基于此代码进行了单元测试。但是,转向“控制器为”语法,不清楚如何修改测试以通过。问题似乎是使用'MyCtrl1 as foo' 是问题所在。甚至 not.toBeDefined 测试也以 TypeError: undefined is not a function 失败。
  • 这里的'spyOn'语句有什么意义。我从测试规范中删除了它,测试仍然成功通过。
  • 据我所知,这个问题是 $http 返回的承诺具有 successerror 辅助函数,而常规的 $q 承诺缺乏这些函数。跨度>
  • @JamesKingsbery $q.then(),这可能是比.success() 更好的处理promise 的方式,并且它具有所有功能和更多。因此,使用.then() 处理$http 结果,您的处理程序也将使用$q
猜你喜欢
  • 2014-03-23
  • 2015-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 2017-06-03
  • 2016-07-25
相关资源
最近更新 更多