2016-02-16 64 views
22

我试图用茉莉花测试我的应用程序,并得到以下问题:
我会计算在我的承诺then功能的东西。这是我需要测试我的代码的地方。茉莉花测试promise.then功能

这里是我的控制器代码:

TestCtrl.$inject = ["$scope", "TestService"]; 
    /* ngInject */ 
    function TestCtrl($scope, TestService) { 
    $scope.loadData = function() { 
     TestService.getData().then(function (response) { 
     $scope.data = response.data; 
     $scope.filtered = $scope.data.filter(function(item){ 
      if(item.id > 1000){ 
      return true; 
      } 
      return false; 
     }) 
     }); 
    } 
    } 

我的茉莉花测试代码:

describe('TestService tests', function() { 
    var $q; 
    beforeEach(function() { 
    module('pilot.fw.user'); 
    }); 
    beforeEach(inject(function (_$q_) { 
    $q = _$q_; 
    })); 
    describe('UserController Tests', function() { 

    beforeEach(inject(function (_$httpBackend_, $rootScope, $controller) { 
     this.scope = $rootScope.$new(); 
     this.$rootscope = $rootScope; 
     this.$httpBackend = _$httpBackend_; 
     this.scope = $rootScope.$new(); 
     var TestServiceMock = { 
     getData: function() { 
      var deferred = $q.defer(); 
      var result = [{ 
      "id": 1720, 
      "user": 1132 
      }, 
      { 
       "id": 720, 
       "user": 132 
      }, { 
       "id": 1721, 
       "user": 1132 
      }]; 
      deferred.promise.data = result; 
      deferred.resolve(result); 
      return deferred.promise; 
     } 
     }; 
     this.controller = $controller('TestCtrl', { 
     '$scope': this.scope, 
     'TestService': TestServiceMock 
     }); 
    })); 

    it('test', function(){ 
     this.scope.loadData(); 
     expect(true).toBeTruthy(); 
    }) 
    }); 
}); 

奇怪的,我不明白的是(与控制台日志测试):

  • 我的承诺已创建并返回
  • 我的承诺数据fu nction被调用,它会调用从TestService的中的getData()函数
  • 一切那么函数内部虽然我回来作为解决

所以,我怎么可以测试里面代码的承诺将不会执行然后功能?
感谢您的帮助

回答

29

“它”方法接受一个进行了参数,你可以拨打电话进行异步测试

it('Should be async', function(done) { 
    someAsyncFunction().then(function(result) { 
    expect(result).toBe(true); 
    done(); 
    }); 
}); 

随意深如你想要去的,只是一定茉莉花调用完成时一切都完成了。茉莉花的默认超时是每个测试5秒,所以如果异步的东西没有完成,那么茉莉花会崩溃。您可以在配置中更改此设置或将其设置在终端中。

这是直接从茉莉文档,向您展示如何处理默认的超时间隔

describe("long asynchronous specs", function() { 
    var originalTimeout; 
    beforeEach(function() { 
    originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 
    jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 
    }); 

    it("takes a long time", function(done) { 
    setTimeout(function() { 
     done(); 
    }, 9000); 
    }); 

    afterEach(function() { 
    jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 
    }); 
}); 

我认为,如果它没有在10秒内工作,你可能有错误的方法。特别是如果你正在与本地服务器/数据库交谈。如果你正在执行HEAVY计算,或者正在用一个不太好的互联网连接打一个外部API,这个东西只应该花费很长时间。如果所有事情都是本地的(或者是嘲弄/嘲弄!),那么超过5-10秒的任何事情都是明确的红旗。

+0

它( '测试',功能(完成){ TestServiceMock.getData(),然后(函数(结果){ 的console.log(结果); }); 期望(真).toBeTruthy(); 完成();} ) 我尝试了这种方式,但我isn't控制台日志甚至被称为......像以前一样 同样的问题:/ - 对不起,CodeStyle - 坦克的帮助 –

+0

你调用DONE “当时”之外! 确保'done'是你当时的方法的内部:) –

+0

否则,你在承诺解决之前就执行了,更不知道结果是什么。请注意,在我的示例中,如何将承诺的结果传递给期望方法?这只在解决时才起作用,它在'then'内 –

0

关于你的控制器,你应该像这样返回值。

TestCtrl.$inject = ["$scope", "TestService"]; 
/* ngInject */ 
function TestCtrl($scope, TestService) { 
    $scope.loadData = function() { 
    // Return this call, since it will return a new promise 
    // This is what let's you do $scope.loadData.then() 
    return TestService.getData().then(function (response) { 
     // What you return in here will be the first argument 
     // of your then method, in the tests/any env 
     // Ex. return 'foo' 
     // will result in .then(result => result === 'foo') //=> true 
     // return one of these, i suggest the data, go SRP! 
     return $scope.data = response.data; 

     // I would do this stuff in a separate function, but you 
     // can return 'filtered' instead if you like. 
     // 
     // $scope.filtered = $scope.data.filter(function(item){ 
     // if(item.id > 1000){ 
     //  return true; 
     // } 
     // return false; 
     // }); 
    }); 
    } 
} 

请记住,调用'then'后的内容并不意味着什么,值必须被称为INSIDE'then'。不是在它之后,或者在它之前。但它里面。像汤姆格林和弗雷迪得到了指尖的可怜的驼鹿一样。

+0

粗糙,我想upvote为FGF参考,但我不认为控制器应该返回任何东西,除非他们返回的东西的视图 –

1

让我告诉你我做什么,对于Angular 1.x和2.x +项目。使用角度测试工具来消除异步测试中的回调/嵌套。在角度1.x中,这意味着使用$ q和$ rootScope。$ apply()的组合。在角度2.x +中,这意味着使用类似fakeAsync的东西。

From the Angular 1.x docs

it('should simulate promise', inject(function($q, $rootScope) { 
    var deferred = $q.defer(); 
    var promise = deferred.promise; 
    var resolvedValue; 

    promise.then(function(value) { resolvedValue = value; }); 
    expect(resolvedValue).toBeUndefined(); 

    // Simulate resolving of promise 
    deferred.resolve(123); 
    // Note that the 'then' function does not get called synchronously. 
    // This is because we want the promise API to always be async, whether or not 
    // it got called synchronously or asynchronously. 
    expect(resolvedValue).toBeUndefined(); 

    // Propagate promise resolution to 'then' functions using $apply(). 
    $rootScope.$apply(); 
    expect(resolvedValue).toEqual(123); 
})); 

的缺点是,你的代码是联系在一起的角度,其优点是,你的代码是平的,它的便携到2.x +!

我是摩卡测试运行者的粉丝,允许我在测试中返回承诺,您可以试着去实现,但也有缺点,就像需要专门为测试修改代码一样。

4

希望此解决方案有所帮助。我发现在测试嘲弄依赖时有用的一种方法。我试图尽可能地评论我所做的事情。

var returnMock, $scope, TestServiceMock, controller; 

beforeEach(module('app')); 

beforeEach(inject(function($controller) { 
    returnMock = { 
     then: jasmine.createSpy(), 
    }; 
    $scope = {}; 
    // first assumption is You are testing TestService extensively, 
    // I don't care about what getData has to do to get results 
    // All I care about is it gets called when I call loadData 
    TestServiceMock = { 
     getData: jasmine.createSpy().and.returnValue(returnMock); 
    }; 

    controller = $controller; 
})); 

it('should load data when loadData function is called and result set is 
under 1000', function() { 
    controller('TestCtrl', { 
     $scope, 
     TestServiceMock 
    }); 
    // another assumption is your data comes back in such a format 
    // perhaps in the actual code check whether data exists and proceed 
    // or do some other action 
    var returnedData = { 
     data: [ 
      { 
       id: 1, 
       name: 'item 1', 
      }, 
     ] 
    } 
    // when I execute the function/method 
    $scope.loadData(); 
    // I expect getData to be called 
    expect(TestServiceMock.getData).toHaveBeenCalled(); 
    // I expect then to be called and the reason is I mocked it 
    expect(returnMock.then).toHaveBeenCalledWith(jasmine.any(Function)); 
    returnMock.then.calls.mostRecent().args[0](returnedData); 
    // expect data on scope to be equal to my mocked data 
    expect($scope.data).toEqual(returnedData.data); 
    // don't expect any result because 1 < 1000 
    expect($scope.filtered).toEqual([]); 
    expect($scope.filtered.length).toEqual(0); 
}); 

it('should load data when loadData function is called and result set is over 1000', 
    function() { 
    controller('TestCtrl', { 
     $scope, 
     TestServiceMock 
    }); 
    var returnedData = { 
     data: [ 
      { 
       id: 1, 
       name: 'item 1', 
      }, 
      { 
       id: 1000, 
       name: 'item 1000', 
      }, 
      { 
       id: 1001, 
       name: 'item 1000', 
      }, 
      { 
       id: 1002, 
       name: 'item 1002', 
      } 
     ] 
    } 
    $scope.loadData(); 
    expect(TestServiceMock.getData).toHaveBeenCalled(); 
    expect(returnMock.then).toHaveBeenCalledWith(jasmine.any(Function)); 
    returnMock.then.calls.mostRecent().args[0](returnedData); 
    expect($scope.data).toEqual(returnedData.data); 
    // expect a result because some entries in the mocked data have id > 1000 
    expect($scope.filtered).toEqual([ 
     { 
      id: 1001, 
      name: 'item 1000', 
     }, 
     { 
      id: 1002, 
      name: 'item 1002', 
     }]); 
    expect($scope.filtered.length).toEqual(2); 
}); 

Official Jasmine Docs解释大部分的概念广泛。希望解决方案可以帮助!

+0

这个答案保存了我的培根。谢谢! –

+1

它让我知道你还有你的培根:) @KrisMolinari – mahadjr