使用$ httpBackend模拟$ http调用更改预期的URL Angular

时间:2015-07-01 09:53:40

标签: javascript angularjs

我正在尝试测试使用$http

的服务
 var APIClient = function($http) {
     this.send = function(data) {
         $http({
             method: data.method,
             url: data.url,
             headers: data.headers,
             data: data.data
         }).success(function(response, status) {
             data.success(response, status);
         }).error(function(response, status) {
             data.error(response, status);
         });
     }
 }

 angular.module('api.client', []).factory('APIClient', ['$http'
     function($http) {
         var client = new APIClient($http);

         return {
             send: function(data) {
                 return client.send(data);
             },
         }

     }
 ]);

和测试

  describe('send', function() {

      var apiClient, $httpBackend;

      beforeEach(module('compare'));

      beforeEach(inject(function($injector) {
          $httpBackend = $injector.get('$httpBackend');
          apiClient = $injector.get('APIClient');
      }));

      it('Should check if send() exists', function() {
          expect(apiClient.send).toBeDefined();
      });

      it('Should send GET request', function(done) {
          var url = '/';

          $httpBackend.expect('GET', url).respond({});

          apiClient.send({
              url: url,
              success: function(data, status) {
                  console.log(status);
                  done();
              },
              error: function(data, status) {
                  console.log(status);
                  done();
              }
          });

          $httpBackend.flush();
      });
  });

但我总是有这个错误

PhantomJS 1.9.8 (Mac OS X) send Should send GET request FAILED
        Error: Unexpected request: GET templates/test.html
        Expected GET /

预期的网址始终是我app.js中的最后一个状态 在这种情况下

// Ionic Starter App

// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js

angular.module('compare',
    [
        'ionic',
        'manager.user',
        'api.client',
        'api.user',
        'api.compare',
        'user.controllers',
        'test.controllers'
    ]
)

    .run(function ($ionicPlatform) {
        $ionicPlatform.ready(function () {
            // Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
            // for form inputs)
            if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
                cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
            }
            if (window.StatusBar) {
                // org.apache.cordova.statusbar required
                StatusBar.styleLightContent();
            }
        });
    })

    .config(function ($stateProvider, $urlRouterProvider) {

        // Ionic uses AngularUI Router which uses the concept of states
        // Learn more here: https://github.com/angular-ui/ui-router
        // Set up the various states which the app can be in.
        // Each state's controller can be found in controllers.js
        $stateProvider

            // setup an abstract state for the tabs directive
            .state('tab', {
                url: "/tab",
                abstract: true,
                templateUrl: "templates/tabs.html"
            })

            // Each tab has its own nav history stack:

            .state('tab.dash', {
                url: '/dash',
                views: {
                    'tab-dash': {
                        templateUrl: 'templates/tab-dash.html',
                        controller: 'DashCtrl'
                    }
                }
            })

            .state('subscription', {
                url: '/subscription',
                templateUrl: 'templates/subscription.html',
                controller: 'SubscriptionCtrl'
            })

            .state('login', {
                url: '/login',
                templateUrl: 'templates/login.html',
                controller: 'LoginCtrl'
            })

            .state('test-compare', {
                url: '/test/compare',
                templateUrl: 'templates/test.html',
                controller: 'TestCompareCtrl'
            })

        // if none of the above states are matched, use this as the fallback
        $urlRouterProvider.otherwise('/login');

    });

我不明白为什么网址正在改变我正在提供/并且测试templates/test.html这始终是最后一个状态模板

4 个答案:

答案 0 :(得分:6)

你的主要问题是这一行:

beforeEach(module('compare'));

您正在此处加载整个应用,而不仅仅是apiClient。从本质上讲,您正在进行全面的集成测试,而不是单元测试。

您应该只加载api.client

beforeEach(module('api.client'));

有用的东西要注意,你也可以这样做:

$httpBackend.whenGET(/templates\/(.*)/).respond('');基本上忽略了由路由器,控制器或指令加载的所有模板。如果你这样做,它仍然不会被视为单元测试,因为你没有严格测试你的APIClient

另一个有用的说明:

你在.run.config内执行的任何内容都不应该是匿名函数,这样你就可以嘲笑它。

这样做的一个例子是:

.config(CompareStateLoader);

CompareStateLoader.$inject = [
    '$stateProvider', 
    '$urlRouterProvider'
];

function CompareStateLoader(
    $stateProvider, 
    $urlRouterProvider
){
    //configure states here 
}

执行此操作将允许您模拟CompareStateLoader并将其加载到测试运行器中。

有关这方面的更多信息,请参阅John Papa的角度风格指南here

答案 1 :(得分:2)

我建议将所有模板编译成JS文件(例如使用grunt&#34; html2js &#34;任务或业力预处理器&#34; ng-html2js < / strong>&#34;)并且对GETing模板没有头痛。

或者您也可以使用 passThrough

$httpBackend.when('GET', /\.html$/).passThrough()

示例 - http://plnkr.co/edit/pbjcDl?p=preview

但我建议使用第一个选项。

答案 2 :(得分:0)

var apiClient, $httpBackend, $loc;

    beforeEach(module('compare'));

    beforeEach(inject(function($injector, $location) {
        $httpBackend = $injector.get('$httpBackend');
        apiClient = $injector.get('APIClient');
        $loc = $location;
    }));


it ('Should send GET request', function(done) {
        expect($loc.path()).toEqual('');
        var url = '/';

        $httpBackend.expect('GET', $loc.path('/')).respond({});

        apiClient.send({
            url: url,
            success: function(data, status) {
                console.log(status);
                done();
            },
            error: function(data, status) {
                console.log(status);
                done();
            }
        });

        $httpBackend.flush();
    });

修改 您应该使用angular-mock来使用$ location

beforeEach(inject(function(_$httpBackend_, APIClient) {
        $httpBackend = _$httpBackend_;
        apiClient = APIClient;
    }));

答案 3 :(得分:0)

在每个块之前添加此行 - $ httpBackend.expect(&#39; GET&#39;,&#34; templates / test.html&#34;)。回复(200);