Есть ли способ в AngularJs протестировать контроллеры с Сервисами и только mock\stub вещи, как $ http?

Я хотел бы протестировать мой контроллер через мои различные службы и обратно и только заглушить данные, которые возвращаются из вызовов в $ http.

Это подход, который я использую в С# с контейнерами IoC, где я только переопределяю зависимости, которые вы хотите заглушить для теста, и оставляйте все остальное как есть.

Есть ли способ сделать это в тестах AngularJs с Jasmine, чтобы я мог увеличить объем моих тестов, чтобы быть больше, чем один контроллер или услуга?

редактировать

Ниже приведен пример, чтобы прояснить подход, который я хочу использовать.

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, а только mocks $ http. В макете $ http я хотел бы проверить, правильно ли вызвал serviceC, и затем сможет возвращать фиктивные данные, которые я могу проверить, правильно обработан службой B и правильно настроен на область действия aController, только проверяя, что ожидаемый результат на $ scope.bЧто-то правильно.

Я знаю, как тестировать каждый компонент отдельно по отдельности, но я хочу протестировать их все вместе, так как хочу написать меньше тестов и только хочу проверить правильность общего желаемого поведения. В С# этот подход позволяет мне изменить детали реализации, не переписывая кучу тестов, и, если возможно, я хотел бы выполнить тот же рабочий процесс.

Редактировать 2 - Рабочее решение ниже. Основываясь на отзывах Уильяма, я создал FiddleJs с рабочим примером, который моделирует мою ситуацию с наличием двух уровней сервисов (Domain и DAL) и только издевательством $ http в службах DAL.

Пример скрипки

/ / --- 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 ************ 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": "[removed_email]",
 "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": "[removed_email]",
 "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 }}
 
 <div ng-repeat="post in posts">{{ post.title }}</div>
</div>
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,
 }
 );
 }));

Вы можете использовать шпионы для проверки методов на serviceB или serviceC, или обычных проверок, предоставленных $httpBackend.

licensed under cc by-sa 3.0 with attribution.