UI路由器干扰$ httpbackend单元测试,angularjs

时间:2014-05-14 12:52:16

标签: javascript angularjs angular-ui karma-runner angular-ui-router

这是一个具有提交功能的控制器:

$scope.submit = function(){   

 $http.post('/api/project', $scope.project)
      .success(function(data, status){
        $modalInstance.dismiss(true);
      })
      .error(function(data){
        console.log(data);
      })
  }
}

这是我的测试

it('should make a post to /api/project on submit and close the modal on success', function() {
    scope.submit();

    $httpBackend.expectPOST('/api/project').respond(200, 'test');

    $httpBackend.flush();

    expect(modalInstance.dismiss).toHaveBeenCalledWith(true);
  });

我得到的错误是:

Error: Unexpected request: GET views/appBar.html

views / appBar.html是我的templateUrl:

 .state('project', {
    url: '/',
    templateUrl:'views/appBar.html',
    controller: 'ProjectsCtrl'
  })

所以不知何故,ui-router正在使我的$ httpBackend指向此而不是我的提交功能。我在使用$ httpBackend的所有测试中遇到了同样的问题。

有没有解决方案?

5 个答案:

答案 0 :(得分:48)

抓住这个要点 https://gist.github.com/wilsonwc/8358542

angular.module('stateMock',[]);
angular.module('stateMock').service("$state", function($q){
    this.expectedTransitions = [];
    this.transitionTo = function(stateName){
        if(this.expectedTransitions.length > 0){
            var expectedState = this.expectedTransitions.shift();
            if(expectedState !== stateName){
                throw Error("Expected transition to state: " + expectedState + " but transitioned to " + stateName );
            }
        }else{
            throw Error("No more transitions were expected! Tried to transition to "+ stateName );
        }
        console.log("Mock transition to: " + stateName);
        var deferred = $q.defer();
        var promise = deferred.promise;
        deferred.resolve();
        return promise;
    }
    this.go = this.transitionTo;
    this.expectTransitionTo = function(stateName){
        this.expectedTransitions.push(stateName);
    }

    this.ensureAllTransitionsHappened = function(){
        if(this.expectedTransitions.length > 0){
            throw Error("Not all transitions happened!");
        }
    }
});

将它添加到test / mock文件夹中名为stateMock的文件中,如果尚未获取该文件,请将其包含在karma配置中。

测试前的设置应如下所示:

beforeEach(module('stateMock'));

// Initialize the controller and a mock scope
beforeEach(inject(function ($state //other vars as needed) {
    state = $state;
    //initialize other stuff
}

然后在你的测试中你应该添加

state.expectTransitionTo('project');

答案 1 :(得分:39)

关于Unit Testing UI Router的这个Github问题更全面地解释了正在发生的事情。

问题是$httpBackend.flush()会触发广播,然后会触发stateProvider的其他情况。

一个简单的解决方案可以是进行以下设置,如上面提到的Github线程中的@darinclark所提到的那样。如果您不需要测试状态转换,则此选项有效。否则,请查看受@rosswil's answer启发的@Vratislav answer on Github

beforeEach(module(function ($urlRouterProvider) {
    $urlRouterProvider.otherwise(function(){return false;});
}));

<强> EDITED

感谢Chris T在评论中报告这一点,似乎是在v0.2.14之后?最好的方法是使用

beforeEach(module(function($urlRouterProvider) {
  $urlRouterProvider.deferIntercept();
}));

答案 2 :(得分:3)

如果您不想添加gist文件,就像在正确的解决方案中所说的那样,您可以添加&#34;当&#34;条件到你的$ httpBackend忽略这样的观点的GET请愿:

$httpBackend.when("GET", function (url) {
    // This condition works for my needs, but maybe you need to improve it
    return url.indexOf(".tpl.html") !== -1;
}).passThrough();

答案 3 :(得分:2)

我有同样的错误你的评论,在通话服务后他们问我关于ui-route的网址。

要解决调用问题,测试中的其他ui-route在之前语句中不注入$ state 。在我的测试中,$ state没有意义使用它。

答案 4 :(得分:1)

将您的服务移动到不依赖于ui.router的模块。让你的主应用依赖于这个模块。当您测试不测试主应用程序时,请测试其中包含您的服务的模块。 stateprovider不会尝试更改状态/路由,因为该模块对ui.router一无所知。这对我有用。