AngularJs中是否有一种方法可以使用Services来测试控制器,只有像$ http这样的模拟\ stub?

时间:2014-08-11 22:23:36

标签: angularjs jasmine

我想通过我的各种服务从我的控制器进行测试并返回,并且只将从调用返回的数据存根到$ http。

这是我在C#中使用IoC容器的一种方法,我只覆盖你想要测试的存根的依赖关系,并保留其他所有内容。

有没有办法在使用Jasmine的AngularJs测试中执行此操作,以便我可以将测试范围扩大到大于单个控制器或服务?

修改

下面是一个澄清我想要使用的方法的示例。

angular.module('app')
    .service('aController', [
        'servcieB',
        'serviceC',
        function ($scope, serviceB, serviceC) {

            serviceC.doHttpSomething().then(function (result) {

                    $scope.bSomething = serviceB.doSomething(result);
            });

        }
    ])
    .service('serviceB', function () {

        function doSomething(input) {

            return input + ' with something';
        };

        return {
            doSomething: doSomething
        }
    })
    .service('serviceC', [
        '$http',
        function (http) {

            function doHttpSomething() {
                return http.get('/something');
            };

            return {
                doHttpSomething: doHttpSomething
            }
        }]);

在上面的设计代码中,我希望能够编写一个测试aController,serviceB和serviceC的测试,同时只能模拟$ http。在$ http模拟器上,我想验证serviceC是否正确调用它然后能够返回虚拟数据然后我可以验证服务是否正确处理并通过aController正确设置到范围,只检查预期结果在$ scope.bSomething是正确的。

我知道如何单独测试每个组件,但我想一起测试它们,因为我想编写更少的测试,只想验证整体所需的行为是否正确。在c#中,这种方法允许我更改实现细节,而不必重写一堆测试,如果可能的话,我想在这里完成相同的工作流程。

编辑2 - 下面的工作解决方案 根据William的反馈,我创建了一个FiddleJs,其中有一个工作示例,用于模拟我有两层服务(Domain和DAL)的情况,并且只模拟DAL服务中的$ http。

Fiddle Example

// --- Js CODE --------------------------

angular.module('app', [])
    .controller('MainController', function ($scope, $log, BlogService) {
    $scope.username = 'Bret';
    $scope.posts = [];

    BlogService.getPostByUserName($scope.username).then(

    function (response) {

        $log.debug('MainController.getPostByUserName reponse.length: ' + response.length);

        for (var i = 0; i < response.length; i++) {
            $scope.posts.push(response[i]);
        }
    });
})
    .factory('BlogService', function ($q, $log, PostService, UserService) {

    function getPostByUserName(userName) {
        var deffered = $q.defer();

        UserService.getUserByUserName(userName).then(

        function (user) {

            $log.debug('BlogService.getUserByUserName.then user:' + user + ' user.id: ' + user.id);

            PostService.getPosts().then(

            function (posts) {
                var postsByUser = [];

                for (var i = 0; i < posts.length; i++) {
                    if (posts[i].userId === user.id) {
                        postsByUser.push(posts[i]);
                    }
                }

                deffered.resolve(postsByUser);
            },

            function (error) {
                deffered.reject(error);
            });
        });

        return deffered.promise;
    };

    return {
        getPostByUserName: getPostByUserName
    };
})
    .factory('PostService', function ($http, $q) {

    function getPosts() {
        var deffered = $q.defer();

        $http.get('http://jsonplaceholder.typicode.com/posts')
            .success(function (response) {
            deffered.resolve(response);
        })
            .error(function (error) {
            deferred.reject(error);
        });

        return deffered.promise;
    };

    return {
        getPosts: getPosts
    };
})
    .factory('UserService', function ($http, $q) {

    function getUserByUserName(userName) {
        var deffered = $q.defer();

        $http.get('http://jsonplaceholder.typicode.com/users')
            .success(function (response) {

            var user;

            for (var i = 0; i < response.length; i++) {
                if (response[i].username === userName) {
                    user = response[i];
                    break;
                }
            }

            deffered.resolve(user);
        })
            .error(function (error) {
            deferred.reject(error);
        });

        return deffered.promise;
    };

    return {
        getUserByUserName: getUserByUserName
    };
});


//--- SPECS -------------------------
describe('app', function () {

    var endpointController;

    var dummyPosts = [
  {
    "userId": 1,
    "id": 1,
    "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
    "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
  }];

    var dummyUsers = [
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771",
      "geo": {
        "lat": "-43.9509",
        "lng": "-34.4618"
      }
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net",
    "company": {
      "name": "Deckow-Crist",
      "catchPhrase": "Proactive didactic contingency",
      "bs": "synergize scalable supply-chains"
    }
  }];

    beforeEach(module('app'));

    it('should return posts by bret ', inject(function ($rootScope, $controller, $httpBackend) {
        var scope = $rootScope.$new();  

 $httpBackend.expectGET('http://jsonplaceholder.typicode.com/users')
            .respond(dummyUsers);
        $httpBackend.expectGET('http://jsonplaceholder.typicode.com/posts')
            .respond(dummyPosts);    

        endpointController = $controller("MainController", { $scope: scope });

        $httpBackend.flush();

        expect(endpointController).not.toBeNull();
        expect(scope.posts.length).toBe(1);
    }));
});

要使示例生效,您可以使用下面的视图代码

// - HTML代码-----------------------

<div ng-app="app" ng-controller="MainController">{{ "Hellow " + message }}
    <br/>
    <div ng-repeat="post in posts">{{ post.title }}</div>
</div>

1 个答案:

答案 0 :(得分:1)

结帐$httpBackend。它允许您指定特定请求的响应。

<强>更新

使用angular.mock.inject完成通常的注射过程。您需要明确连接范围:

    var ctrl; //controller under test
    beforeEach(angular.mock.inject(function($rootScope, $controller, serviceB, serviceC) {
        scope = $rootScope.$new;
        ctrl = $controller('RatioBoxController', {
                $scope: scope,
                serviceB: serviceB,
                serviceC: serviceC,
            }
        );
    }));

您可以使用间谍来验证调用serviceBserviceC的方法,或$httpBackend提供的正常检查。