2015-07-01 8 views
20

Próbuję przetestować usługę, która korzysta $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); 
      }, 
     } 

    } 
]); 

a test

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(); 
     }); 
    }); 

Ale zawsze mam ten błąd

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

oczekiwany URL jest zawsze ostatni stan w moim app.js W tym przypadku

// 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'); 

    }); 

Nie rozumiem dlaczego url zmienia daję / i przetestować templates/test.html który jest zawsze ostatni szablon stan

+0

Po wstrzyknięciu '$ httpBackend' w naszych testach przechwytuje wszystkie żądania HTTP. Więc oczywiście masz jakiś kod (nie od APIClient, jeśli jest to jedyny jego kod, który jest przedstawiony w OP), który wyzwala stany. Czy możesz podać kod, który definiuje moduł "porównać"? –

+0

@KirillSlatin Właśnie zaktualizowałem ostatni kod całego app.js – Ajouve

+0

Dlaczego deklarujesz usługę 'APIClient' w module' api.client', ale wywołujesz 'compare' w pakiecie testowym dla usługi? –

Odpowiedz

6

Twoje główne słówka ue tu jest ta linia:

beforeEach(module('compare')); 

ładujesz całą aplikację tutaj zamiast tylko apiClient. Zasadniczo wykonujesz pełny test integracyjny zamiast testu jednostkowego.

Powinieneś ładować tylko api.client.

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

coś pożytecznego, aby pamiętać, można też zrobić coś takiego:

$httpBackend.whenGET(/templates\/(.*)/).respond(''); które zasadniczo ignoruje wszystkie szablony, które są ładowane przez routery, kontrolery lub dyrektyw. Jeśli jednak to zrobisz, nadal nie będzie to traktowane jako test jednostkowy, ponieważ nie musisz dokładnie testować swojego APIClient.

Innym przydatnym uwaga:

Cokolwiek wykonać wewnątrz .run lub .config nie powinno być annonymous funkcja, w ten sposób można go wyśmiewać.

Przykładem tego robiłbym:

.config(CompareStateLoader); 

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

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

Rozwiązanie to pozwoli Ci drwić CompareStateLoader i załadować to w swoim testowym biegacza.

Aby uzyskać więcej informacji na ten temat, zobacz Przewodnik po kątach John Papa here.

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(); 
    }); 

Edit Należy użyć kątowe-udawaj, aby użyć $ lokalizacja

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

Proszę spróbuj z 'spodziewać ($ loc.path()). DoEqual ('');' i daj mi opinię – gr3g

+0

'oczekiwać ($ loc.path()). DoEqual (" '); "działa ale '$ loc.path ('/')' zwraca obiekt i mam błąd 'Oczekiwano GET [object Object]' i '$ loc.path ('/'). url()' zwraca '/' – Ajouve

+0

I "Nie używam routera UI, może to być przyczyną błędu – gr3g

1

Proponuję połączyć wszystkie swoje szablony do pliku JS (na przykład z pomrukiem „html2js” zadaniowych lub karma preprocesora „ng-html2js”) i nie mają głowy z czuło szablonów.

Albo też przydałby PASSTHROUGH

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

Przykład - http://plnkr.co/edit/pbjcDl?p=preview

Ale proponuję użyć pierwszą opcję.

0

Dodaj tę linię w swoim przed każdym blokiem - $ httpBackend.expect ("GET", "templates/test.html") .odpowiada (200);

Powiązane problemy