UI-router与$ http后端单元测试、有棱角的js进行交互

时间:2021-07-23 10:49:13

This is a controller with a submit function:

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

$scope.submit = function(){   

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

This is my test

这是我的测试

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

The error I get is:

我得到的错误是:

Error: Unexpected request: GET views/appBar.html

views/appBar.html is my templateUrl:

视图/ appBar。html是我templateUrl:

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

So somehow ui-router is making my $httpBackend point to this instead of my submit function. I have the same issue in all my tests using $httpBackend.

所以ui-router把$ http后台指向这个而不是提交函数。我在使用$ http后台的所有测试中都有同样的问题。

Is there any solution to this?

有什么解决办法吗?

5 个解决方案

#1


48  

Take this gist https://gist.github.com/wilsonwc/8358542

把这个要点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!");
        }
    }
});

Add it to a file called stateMock in your test/mock folder, include that file in your karma config if it isn't already picked up.

将它添加到测试/模拟文件夹中名为stateMock的文件中,如果这个文件还没有被选中,则将它包含在karma配置中。

The setup before your test should then look something like this:

测试前的设置应该是这样的:

beforeEach(module('stateMock'));

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

Then in your test you should add

那么在你的测试中你应该加上

state.expectTransitionTo('project');

#2


39  

This Github issue about Unit Testing UI Router explains more fully what's happening.

这个Github关于单元测试UI路由器的问题更充分地解释了正在发生的事情。

The problem is that $httpBackend.flush() triggers a broadcast which then triggers the otherwise case of the stateProvider.

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

A simple solution can be to do the following setup, as mentionned by @darinclark in Github thread mentionned above. This is valid if you do not need to test state transitions. Otherwise have a look to @rosswil's answer that is inspired by @Vratislav answer on Github.

一个简单的解决方案可以是执行以下设置,如@darinclark在Github线程中提到的那样。如果不需要测试状态转换,这是有效的。或者看看@rosswil的答案,这是受到@Vratislav对Github的回答的启发。

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

EDITED

编辑

Thanks to Chris T to report this in the comments, seems after v0.2.14? the best way to do this is to use

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

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

#3


3  

If you don't want to add gist files like it says in the correct solution you can add a "when" condition to your $httpBackend to ignore GET petitions of views like this:

如果您不想像在正确的解决方案中所说的那样添加gist文件,您可以在$ httpback中添加“when”条件,以忽略这样的视图请求:

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

#4


2  

I have the same error your commented, after a call service they ask me about the url of otherwise ui-route.

我有同样的错误你的评论,在一个呼叫服务之后,他们问我关于其他ui-route的url。

To solve the problem of call the otherwise ui-route in testing is not inject $state in beforeach statment. In my testing $state not have sense to use it.

要解决在测试中调用其他ui路径的问题,不需要在每个状态之前注入$state。在我的测试中,$state没有使用它的意义。

#5


1  

Move your services to their own module that have no dependency on ui.router. have your main app depend on this module. When you test don’t test the main app, test the module that has your services in it. The stateprovider won’t try to change state/route because this module knows nothing about the ui.router. This worked for me.

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

#1


48  

Take this gist https://gist.github.com/wilsonwc/8358542

把这个要点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!");
        }
    }
});

Add it to a file called stateMock in your test/mock folder, include that file in your karma config if it isn't already picked up.

将它添加到测试/模拟文件夹中名为stateMock的文件中,如果这个文件还没有被选中,则将它包含在karma配置中。

The setup before your test should then look something like this:

测试前的设置应该是这样的:

beforeEach(module('stateMock'));

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

Then in your test you should add

那么在你的测试中你应该加上

state.expectTransitionTo('project');

#2


39  

This Github issue about Unit Testing UI Router explains more fully what's happening.

这个Github关于单元测试UI路由器的问题更充分地解释了正在发生的事情。

The problem is that $httpBackend.flush() triggers a broadcast which then triggers the otherwise case of the stateProvider.

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

A simple solution can be to do the following setup, as mentionned by @darinclark in Github thread mentionned above. This is valid if you do not need to test state transitions. Otherwise have a look to @rosswil's answer that is inspired by @Vratislav answer on Github.

一个简单的解决方案可以是执行以下设置,如@darinclark在Github线程中提到的那样。如果不需要测试状态转换,这是有效的。或者看看@rosswil的答案,这是受到@Vratislav对Github的回答的启发。

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

EDITED

编辑

Thanks to Chris T to report this in the comments, seems after v0.2.14? the best way to do this is to use

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

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

#3


3  

If you don't want to add gist files like it says in the correct solution you can add a "when" condition to your $httpBackend to ignore GET petitions of views like this:

如果您不想像在正确的解决方案中所说的那样添加gist文件,您可以在$ httpback中添加“when”条件,以忽略这样的视图请求:

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

#4


2  

I have the same error your commented, after a call service they ask me about the url of otherwise ui-route.

我有同样的错误你的评论,在一个呼叫服务之后,他们问我关于其他ui-route的url。

To solve the problem of call the otherwise ui-route in testing is not inject $state in beforeach statment. In my testing $state not have sense to use it.

要解决在测试中调用其他ui路径的问题,不需要在每个状态之前注入$state。在我的测试中,$state没有使用它的意义。

#5


1  

Move your services to their own module that have no dependency on ui.router. have your main app depend on this module. When you test don’t test the main app, test the module that has your services in it. The stateprovider won’t try to change state/route because this module knows nothing about the ui.router. This worked for me.

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