Javascript Jasmine 测试一个 promise.then 函数
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35430827/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Jasmine test a promise.then function
提问by Jonas Hans
I try to test my app with Jasmine and got the following problem:
I will calculate something in the then
function of my promise. That's the point where I need to test my code.
我尝试用 Jasmine 测试我的应用程序并遇到以下问题:
我将在then
我的承诺函数中计算一些东西。这就是我需要测试我的代码的地方。
Here is the code of my controller:
这是我的控制器的代码:
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;
})
});
}
}
And my Jasmine test code:
还有我的 Jasmine 测试代码:
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();
})
});
});
The strange thing I don't understand is (tested with console logs):
我不明白的奇怪事情是(用控制台日志测试):
- My promise is created and returned
- My loadData function is called and it will call the getData() function from the TestService
- Everything inside the then function won't be executed although I return the promise as resolved
- 我的承诺被创建并返回
- 我的 loadData 函数被调用,它将从 TestService 调用 getData() 函数
- 尽管我将承诺返回为已解决,但不会执行 then 函数中的所有内容
So how could I test the code inside the thenfunction?
Thanks for help
那么如何测试then函数中的代码呢?
感谢帮助
回答by Dustin Stiles
the jasmine 'it' method takes a done parameter that you can call for async testing
jasmine 'it' 方法采用 done 参数,您可以调用该参数进行异步测试
it('Should be async', function(done) {
someAsyncFunction().then(function(result) {
expect(result).toBe(true);
done();
});
});
Feel free to go as deep as you want, just be sure to call done when EVERYTHING is finished. Jasmine's default timeout is 5 seconds per test, so if the async stuff isn't done by then jasmine will crash. You can change this setting in the configs or set it in the terminal.
随意深入,只要确保在一切都完成后调用完成。Jasmine 的默认超时时间是每次测试 5 秒,所以如果异步操作没有完成,那么 jasmine 就会崩溃。您可以在配置中更改此设置或在终端中进行设置。
This is straight from the jasmine docs, showing you how to handle the default timeout interval
这直接来自 jasmine 文档,向您展示了如何处理默认超时间隔
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;
});
});
I think that if it doesn't work in 10 seconds, you may have faulty methods. ESPECIALLY if you are talking to a local server / db. This stuff should only take this long if you are performing HEAVY computations, or are hitting an external api with a not-so-great internet connection. If everything is local (or stubbed / mocked!) then anything over 5-10 seconds is a definite red flag.
我认为如果它在 10 秒内不起作用,则可能是您的方法有问题。特别是如果您正在与本地服务器/数据库交谈。如果您正在执行繁重的计算,或者正在使用不太好的互联网连接访问外部 api,那么这些东西应该只需要这么长时间。如果一切都是本地的(或存根/嘲笑!),那么超过 5-10 秒的任何事情都是一个明确的危险信号。
回答by mahad
hope this solution helps. One approach I've found useful when testing is mocking dependencies. I've tried to comment out what I've done as much as possible.
希望这个解决方案有帮助。我发现在测试时有用的一种方法是模拟依赖项。我试图尽可能多地评论我所做的事情。
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 Docsexplain most of the concepts extensively. Hope the solution helps!!!!
官方 Jasmine Docs 对大多数概念进行了广泛的解释。希望解决方法有帮助!!!!
回答by Jacob McKay
Let me tell ya what I do, for Angular 1.x and 2.x+ projects. Use the angular testing tools to get rid of callbacks/nests in your async tests. In angular 1.x, that means using a combination of $q and $rootScope.$apply(). In angular 2.x+, that means using something like fakeAsync.
让我告诉你我为 Angular 1.x 和 2.x+ 项目做什么。使用角度测试工具摆脱异步测试中的回调/嵌套。在 angular 1.x 中,这意味着使用 $q 和 $rootScope.$apply() 的组合。在 angular 2.x+ 中,这意味着使用类似 fakeAsync 的东西。
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);
}));
The disadvantage is that your code is tied to angular, the advantages are that your code is flat and it's portable to 2.x+!
缺点是您的代码与 angular 相关,优点是您的代码是扁平的并且可以移植到 2.x+!
I was a fan of the mocha test runner that allowed me to return promises in my tests, you could try to get that going, but there are downsides to that as well like needing to modify your code specifically for a test.
我是 mocha 测试运行器的粉丝,它允许我在我的测试中返回承诺,你可以尝试让它继续,但也有缺点,比如需要专门为测试修改代码。
回答by Yevheniy Potupa
You better watch this https://codecraft.tv/courses/angular/unit-testing/asynchronous/
你最好看这个https://codecraft.tv/courses/angular/unit-testing/asynchronous/
You have actually 3 ways:
你实际上有 3 种方法:
1) use regular it:
1)使用常规它:
it('test', (done) => {
const spy = spyOn(func, 'bar').and.returnValue(Promise.resolve(true));
spy.calls.mostRecent().returnValue.then(res => {
...your expect here...
done();
})
} );
2) use async in beforeEach and it:
2) 在 beforeEach 中使用 async 和它:
it('test', async(() => {
spyOn(func, 'bar').and.returnValue(Promise.resolve(true));
fixture.whenStable().then(res => {
...your expect here...
})
} ));
3) use fakeAsync if you don't have Http or XHR calls:
3) 如果您没有 Http 或 XHR 调用,请使用 fakeAsync:
it('test', fakeAsync(() => {
spyOn(func, 'bar').and.returnValue(Promise.resolve(true));
tick();
...your expect here...
} ));
回答by Dustin Stiles
In regards to your controller, you should 'return' values like so.
关于您的控制器,您应该像这样“返回”值。
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;
// });
});
}
}
Remember that calling something AFTER 'then' doesn't mean anything, values must be called INSIDE 'then'. Not after it, or before it. But inside it. Like Tom Green and that poor moose in Freddy Got Fingered.
请记住,在 'then' 之后调用某些东西并不意味着什么,值必须在 INSIDE 'then' 中调用。不是在它之后,也不是在它之前。但是里面。就像 Tom Green 和 Freddy Got Fingered 中那只可怜的驼鹿一样。