【问题标题】:UI-router interfers with $httpbackend unit test, angular jsUI-router 干扰 $httpbackend 单元测试,角度 js
【发布时间】:2016-04-14 15:02:44
【问题描述】:

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

$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 的测试中都遇到了同样的问题。

有什么解决办法吗?

【问题讨论】:

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


【解决方案1】:

抓住这个要点 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');

【讨论】:

  • 太棒了!我几乎一起跳过了ui-router。做得好!您应该与 ui-router 人员分享这项工作,以便人们更好地找到它。
  • @user1572526 是啊 太棒了! +1 rosswil 只是一个巨大的头痛 ^^ 在这里看到很多人对我来说似乎很奇怪....
  • 哇,这需要与 ui-router 共享,花了几个小时试图确定为什么注释掉我的 $urlRouterProvider 正在修复我的测试。感谢 rosswil 和 Whisher 指点我这里!!!
  • 使用 ui-router v0.2.11 (1937:19) 获取“TypeError: Cannot read property '$current' of undefined”。 registerState 方法崩溃,$state 未定义。有人有同样的问题吗?
  • 这可以用来测试使用$state.go 的转换是否传递了正确的$stateParams?即:测试$state.go('users', {name: 'Bob' }) 是否正确通过{name: 'Bob'} 作为过渡的一部分?
【解决方案2】:

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

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

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

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

已编辑

感谢 Chris T 在 cmets 中报告此问题,似乎在 v0.2.14 之后?最好的方法是使用

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

【讨论】:

  • UI-Router 现在有一个$urlRouterProvider.deferIntercept() 函数。见github.com/angular-ui/ui-router/issues/…
  • 这是最快的解决方案
  • @ChrisT,你的修复是最优雅的。感谢您发布它!
  • 问题是 $httpBackend.flush() 触发了一个广播,然后触发了 stateProvider 的其他情况。可能正是大多数人正在发生的事情。
  • $urlRouterProvider 自 v1.0.0 起已弃用。请改用$urlServiceProvider.deferIntercept();。
【解决方案3】:

如果您不想像正确解决方案中所说的那样添加 gist 文件,您可以在 $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();

【讨论】:

  • 我收到.passThrough is not a function 错误,不知道为什么。这对我有用:$httpBackend.whenGET(/templates\/.*/).respond('');
  • 您可以在这里查看文档,passThrough 是一个有效函数:docs.angularjs.org/api/ngMockE2E/service/$httpBackend 是 requestHandler 的一个函数,它是“when”函数的答案。
  • 即使我得到 .passThrough 也不是函数错误。 '.respond' 工作正常。
  • .passThrough 可用于角度 e2e 测试 (ngMockE2E),但不可用于单元测试 (ngMock)
【解决方案4】:

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

为了解决在测试中调用otherwise ui-route 的问题是not injection $state in beforeach 语句。在我的测试中 $state 没有使用它的意义。

【讨论】:

    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-01-02
      • 1970-01-01
      • 1970-01-01
      • 2014-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多