Жасмин проверяет обещание.
Я пытаюсь проверить приложение с помощью Jasmine и получил следующую проблему:
Я посчитаю что-то в функции 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;
})
});
}
}
И мой тестовый код 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();
})
});
});
Странная вещь, которую я не понимаю, (протестирован с консольными журналами):
- Мое обещание создано и возвращено
- Вызывается функция loadData и вызывается функция getData() из TestService
- Все внутри функции then не будет выполнено, хотя я верну обещание как разрешенное
Итак, как я могу проверить код внутри функции then?
Спасибо за помощь
Ответы
Ответ 1
метод jasmine 'it' принимает сделанный параметр, который вы можете вызвать для асинхронного тестирования
it('Should be async', function(done) {
someAsyncFunction().then(function(result) {
expect(result).toBe(true);
done();
});
});
Не стесняйтесь идти так глубоко, как вы хотите, просто обязательно позвоните, когда все будет закончено. Тайм-аут по умолчанию Jasmine составляет 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 секунд, у вас могут быть неисправные методы. ESPECIALLY, если вы разговариваете с локальным сервером /db. Этот материал должен длиться так долго, только если вы выполняете HEAVY-вычисления или нажимаете внешний api с не очень-то отличным подключением к Интернету. Если все локально (или обрезано/издевается!), То что-либо более 5-10 секунд является определенным красным флагом.
Ответ 2
надеюсь, что это решение поможет. Один из подходов, который я нашел полезным при тестировании, - это проверка зависимостей Я пытался прокомментировать, что я сделал как можно больше.
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);
});
Официальные документы по Жасмин подробно объясняют большинство понятий. Надеюсь, что решение помогает !!!!
Ответ 3
Позвольте мне сказать, что я делаю, для проектов Angular 1.x и 2.x+. Используйте инструменты тестирования Angular, чтобы избавиться от обратных вызовов/гнезд в ваших тестах асинхронного тестирования. В Angular 1.x это означает использование комбинации $q и $rootScope. $Apply(). В Angular 2.x + это означает использование чего-то вроде fakeAsync.
Из 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);
}));
Недостаток заключается в том, что ваш код привязан к angular, преимущества в том, что ваш код плоский и он переносится на 2.x +!
Я был поклонником тест-лидера мокки, который позволил мне вернуть promises в моих тестах, вы можете попытаться добиться этого, но есть и недостатки, а также необходимость изменения кода специально для теста.
Ответ 4
Что касается вашего контроллера, вы должны "вернуть" значения таким образом.
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 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". Не после него, или до него. Но внутри. Как Том Грин и этот бедный лосей в Фредди Готли.
Ответ 5
Вам лучше посмотреть это https://codecraft.tv/courses/angular/unit-testing/asynchronous/
У вас есть 3 способа:
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) используйте async в beforeEach, и это:
it('test', async(() => {
spyOn(func, 'bar').and.returnValue(Promise.resolve(true));
fixture.whenStable().then(res => {
...your expect here...
})
} ));
3) используйте fakeAsync, если у вас нет вызовов Http или XHR:
it('test', fakeAsync(() => {
spyOn(func, 'bar').and.returnValue(Promise.resolve(true));
tick();
...your expect here...
} ));