Ошибка: Тайм-аут - обратный вызов Async не вызывается в течение указанного тайм-аута.....DEFAULT_TIMEOUT_INTERVAL
У меня есть класс обслуживания angular: -
angular.module('triggerTips')
.service('userData', function ($rootScope, $http, $log, $firebase) {
this._log = {
service : 'userData'
};
// Synchronized objects storing the user data
var config;
var userState;
// Loads the user data from firebase
this.init = function(readyCallback) {
var log = angular.extend({}, this._log);
log.funct = 'init';
var fireRef = new Firebase('https://XYZfirebaseio.com/' + $rootScope.clientName);
config = $firebase(fireRef.child('config')).$asObject();
userState = $firebase(fireRef.child('userState').child($rootScope.userName)).$asObject();
Promise.all([config.$loaded(), userState.$loaded()]).
then(
function() {
if(config == null || Object.keys(config).length < 4) {
log.message = 'Invalid config';
$log.error(log);
return;
}
if(!userState.userProperties) {
userState.userProperties = {};
}
if(!userState.contentProperties) {
userState.contentProperties = {};
}
log.message = 'User Properties: ' + JSON.stringify(userState.userProperties);
$log.debug(log);
log.message = 'Content Properties: ' + JSON.stringify(userState.contentProperties);
$log.debug(log);
log.message = 'Loaded user data from firebase';
$log.debug(log);
readyCallback();
},
function() {
log.message = 'Unable to load user data from firebase';
$log.error(log);
}
);
};
// Returns the initial tip configuration
this.getConfig = function() {
return config;
};
// Set the value of a user property
// A user property is something about the user himself
this.setUserProperty = function(property, value) {
if(!userState.userProperties) {
userState.userProperties = {};
}
userState.userProperties[property] = value;
userState.$save();
$rootScope.$broadcast('user-property-change', property);
};
// Get the value of a user property
this.getUserProperty = function(property) {
if(userState.userProperties) {
return userState.userProperties[property];
}
};
// Set the value of a user content property
// A content property is something about a particular peice of content for a particular user
this.setContentProperty = function(contentName, property, value) {
if(!userState.contentProperties[contentName]) {
userState.contentProperties[contentName] = {};
}
userState.contentProperties[contentName][property] = value;
userState.$save();
$rootScope.$broadcast('content-property-change', contentName, property);
};
// Increment a count property on the user state for a given tip
this.incrementContentProperty = function(contentName, property) {
if(!userState.contentProperties[contentName]) {
userState.contentProperties[contentName] = {};
}
if(!userState.contentProperties[contentName][property]) {
userState.contentProperties[contentName][property] = 0;
}
userState.contentProperties[contentName][property]++;
userState.$save();
$rootScope.$broadcast('content-property-change', contentName, property);
};
// Returns the user state for a given tip and property
this.getContentProperty = function(contentName, property) {
if(userState.contentProperties) {
var t = userState.contentProperties[contentName];
if(t) {
return t[property];
}
}
};
});
Я пытаюсь использовать unit test эту услугу с помощью жасмина: -
my unit test: -
'use strict';
describe('Service: userData', function () {
// load the service module
beforeEach(function() {
module('triggerTips');
});
// instantiate service
var userData;
beforeEach(inject(function (_userData_) {
userData = _userData_;
}));
it('should load correctly', function () {
expect(!!userData).toBe(true);
});
describe('after being initialized', function () {
beforeEach(function(done) {
// Unable to get this working because the callback is never called
userData.init(function() {
done();
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
});
it('should have a valid config', function (done) {
setTimeout(function() {
expect(Object.keys(userData.getConfig()).length == 0);
done();
}, 1500);}); }); });
Я читал об асинхронной поддержке в Жасмине, но поскольку я довольно новичок в модульном тестировании с использованием JavaScript, я не смог заставить его работать.
Я получаю сообщение об ошибке:
Асинхронный обратный вызов не был вызван в течение таймаута, указанного в jasmine.DEFAULT_TIMEOUT_INTERVAL
Может ли кто-нибудь помочь мне предоставить рабочий пример моего кода с некоторым объяснением?
Ответы
Ответ 1
Я бы предложил заменить setTimeout
на $timeout
, чтобы ускорить работу вашего пакета спецификаций. Вам понадобится ngMock, чтобы быть частью вашего набора спецификаций, чтобы заставить это работать по-своему, но это, похоже, уже позаботился о том, чтобы посмотреть на вашу спецификацию. Хороший материал.
Затем, чтобы асинхронный характер спецификации "ушел", вы бы назвали:
$timeout.flush([delay]) где delay
не является обязательным.
- Если никакая задержка не прошла, все ожидающие задачи async (внутри мира angular) завершат то, что они делают.
- Если задержка пройдена, все ожидающие задачи в пределах указанной задержки завершатся. Те, кто находится за пределами указанной задержки, останутся в состоянии ожидания.
С помощью этого вы можете удалить обратный вызов done
и написать свои тесты как таковые:
describe('after being initialized', function () {
var $timeout;
beforeEach(function () {
// Unable to get this working because the callback is never called
userData.init();
inject(function ($injector) {
$timeout = $injector.get('$timeout');
});
}));
it('should have a valid config', function () {
$timeout.flush();
// callback should've been called now that we flushed().
expect(Object.keys(userData.getConfig()).length).toEqual(0);
});
});
Какую Promise
реализацию вы используете? Я вижу вызов Promise.all
, но ради продолжения с моим ответом я собираюсь предположить, он эквивалентен $q.all
. Выполнение $timeout.flush
должно позаботиться об устранении этих значений.
Если вы хотите написать ожидания от отклоненных/разрешенных значений обещания в Жасмине, я бы посмотрел на что-то вроде jasmine-promise-matchers на сделайте его чистым и красивым, но запретите, чтобы вы могли сделать что-то вроде этого:
// $q
function get () {
var p1 = $timeout(function () { return 'x'; }, 250);
var p2 = $timeout(function () { return 'y'; }, 2500);
return $q.all([p1, p2]);
}
// expectation
it('is correct', function () {
var res;
get().then(function (r) {
res = r;
});
$timeout.flush(2500);
expect(res).toEqual(['x', 'y']);
});
В зависимости от вашей установки вам может понадобиться или не понадобиться затухать/шпионить (в зависимости от определения фреймворка шпиона) обещание относительно вашей локальной переменной config
, но эта другая история в целом я считаю.
Я совсем не знаком с $firebase (что-то). $asObject. $loaded - как таковой, возможно, я что-то пропустил, но, полагая, что он работает так же, как и любое другое обещание, вам должно быть хорошо идти.
jsfiddle