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

IT技术 javascript angularjs angular-ui karma-runner angular-ui-router
2021-03-09 04:53:53

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

$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 指向 this 而不是我的提交函数。我在所有使用 $httpBackend 的测试中都遇到了同样的问题。

有什么解决办法吗?

5个回答

拿这个要点 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');
@user1572526 是的,太棒了!+1 rosswil 只是一个巨大的头痛 ^^ 在这里看到很多人对我来说似乎很奇怪......
2021-04-24 04:53:53
惊人的!我几乎一起跳过了 ui-router。做得好!您应该与 ui-router 人员分享这项工作,以便人们可以更好地找到它。
2021-05-06 04:53:53
使用 ui-router v0.2.11 (1937:19) 获取“类型错误:无法读取未定义的属性 '$current'”。registerState 方法崩溃,$state 未定义。有没有人有同样的问题?
2021-05-15 04:53:53
哇,这需要与 ui-router 共享,花了几个小时试图确定为什么注释掉我的 $urlRouterProvider 正在修复我的测试。感谢 rosswil 和 Whisher 将我指向这里!!!
2021-05-16 04:53:53
这可以用来测试$stateParams在使用$state.go? 即:测试$state.go('users', {name: 'Bob' })是否{name: 'Bob'}作为转换的一部分正确通过
2021-05-16 04:53:53

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

问题是$httpBackend.flush()触发广播,然后触发stateProvider.

一个简单的解决方案可以是进行以下设置,正如上面提到的 Github 线程中的@darinclark 所提到的。如果您不需要测试状态转换,这是有效的。否则看看@roswil 的答案,其灵感来自Github 上@Vratislav 答案

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

已编辑

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

beforeEach(module(function($urlRouterProvider) {
  $urlRouterProvider.deferIntercept();
}));
问题是 $httpBackend.flush() 触发广播,然后触发 stateProvider 的其他情况。可能正是大多数人正在发生的事情。
2021-04-23 04:53:53
$urlRouterProvider自 v1.0.0 起已弃用。使用$urlServiceProvider.deferIntercept();来代替。
2021-04-25 04:53:53
UI-Router 现在有一个$urlRouterProvider.deferIntercept()功能。参见github.com/angular-ui/ui-router/issues/...
2021-04-27 04:53:53
这是最快的解决方案
2021-05-02 04:53:53
@ChrisT,你的修复是最优雅的。感谢发布!
2021-05-07 04:53:53

如果您不想添加正确解决方案中所说的要点文件,您可以在 $httpBackend 中添加一个“when”条件来忽略这样的视图的 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();
你可以在这里看到文档, passThrough 是一个有效的函数:docs.angularjs.org/api/ngMockE2E/service/$httpBackend是 requestHandler 的一个函数,它是“when”函数的答案。
2021-04-17 04:53:53
.passThrough在角度 e2e 测试 ( ngMockE2E) 中可用,但在单元测试 ( ngMock) 中不可用
2021-04-30 04:53:53
即使我得到 .passThrough 也不是函数错误。'.respond' 工作得很好。
2021-05-14 04:53:53
我有.passThrough is not a function错误,不知道为什么。这对我有用:$httpBackend.whenGET(/templates\/.*/).respond('');
2021-05-16 04:53:53

我有您评论的相同错误,在呼叫服务后,他们向我询问其他 ui-route 的 url。

为了解决在测试调用others ui-route 的问题,不是beforeach语句中注入$state在我的测试中 $state 没有意义使用它。

将您的服务移至不依赖于 ui.router 的自己的module。让你的主应用依赖这个module。测试时不要测试主应用程序,而是测试其中包含您的服务的module。stateprovider 不会尝试更改状态/路由,因为该module对 ui.router 一无所知。这对我有用。