#javascript #angularjs #unit-testing #jasmine
#javascript #angularjs #модульное тестирование #jasmine
Вопрос:
Для определенных целей мне пришлось написать фабричный метод, который возвращает $timeout
обещание, которое внутри него возвращает $http.get
обещание.
Я хочу проверить, вызовет ли вызов моего фабричного метода $http.get
с правильным поддельным URL ( mocked-get-path
).
Вот мой заводской код:
(function() {
'use strict';
angular.module('MyApp', []);
angular.module('MyApp')
.constant("ROUTES", {
get: "real-get-path"
});
angular.module('MyApp')
.factory('MyFactory', ['$http', '$timeout', 'ROUTES', MyFactory]);
function MyFactory($http, $timeout, ROUTES) {
return {
myGet: function(id) {
var random = Math.random() * 1000;
return $timeout(function () {
return $http.get(ROUTES.get, {
params: { id: id }
})
.then(function() {
return response.data;
});
}, random);
}
};
}
})();
И моя спецификация теста:
describe('simple factory test', function() {
var $http, $timeout, scope, MyFactory;
var ROUTES = {
get: 'mocked-get-path'
};
beforeEach(module('MyApp', function ($provide) {
$provide.constant('ROUTES', ROUTES);
}));
beforeEach(module('MyApp'));
beforeEach(inject(function(_$rootScope_, _$http_, _$timeout_, _MyFactory_) {
scope = _$rootScope_.$new();
$http = _$http_;
$timeout = _$timeout_;
MyFactory = _MyFactory_;
}));
it('should use ROUTES.get on method myGet', function(done) {
spyOn(Math, "random").and.returnValue(0.01);
MyFactory.myGet('elem1')
.then(function(res) {
expect($http.get).toHaveBeenCalledWith(ROUTES.get);
done();
});
$timeout.flush();
});
});
Вы можете видеть, что я пытался написать expect
для $http.get
внутри then
, но это не сработало.
Я получаю эту ошибку от Jasmine:
Ошибка: Неожиданный запрос: ПОЛУЧИТЬ mocked-get-path?id=elem1
Я создал плунжер:https://plnkr.co/edit/Ia6Q6GvKZOkNU2B8GrO1
Что я делаю не так?
Ответ №1:
При тестировании $http
вы захотите использовать $httpBackend.when
В вашем случае:
it('should use ROUTES.get on method myGet', function(done) {
spyOn(Math, "random").and.returnValue(0.01);
spyOn($http, "get").and.callThrough();
MyFactory.myGet('elem1')
.then(function(res) {
expect($http.get).toHaveBeenCalledWith(ROUTES.get, {params: {id: 'elem1'}});
done();
});
$httpBackend
.when('GET', "mocked-get-path?id=elem1")
.respond(200, { foo: 'bar' });
$timeout.flush(100);
$timeout.verifyNoPendingTasks();
$httpBackend.flush();
});
Это приведет к тому, что $httpBackend
ваш запрос будет успешно завершен, что позволит вашему .then
с ожидаемым выполнить.
Я надеюсь, что это поможет! Хорошего дня!
Комментарии:
1. Спасибо за ваш ответ, но я пробовал что-то подобное и не сработало. Пожалуйста, проверьте эту модификацию, которую я написал с вашим предложением: plnkr.co/edit/RwD3y9OmxSEywUhq2mUh
2. Я решил это. В моем решении отсутствовал $ httpBackend. flush() после $timeout. flush()
3. Большое вам спасибо! Вот модификация: plnkr.co/edit/YvF3Lem8pYRmm0SpCke2
Ответ №2:
Один из подходов заключается в добавлении ожидаемых вызовов к httpBackend следующим образом:
var $httpBackend;
beforeEach(function () {
inject(function ( _$httpBackend_ ) {
$httpBackend = _$httpBackend_;
}
….
it('....', function(){
//before the rest of the test
$httpBackend.expectGET('url').respond("OK");
});
https://docs.angularjs.org/api/ngMock/service /$httpBackend
Итак, ваш код будет выглядеть следующим образом:
describe('simple directive', function() {
var $http, $timeout, scope, MyFactory, $httpBackend;
var ROUTES = {
get: 'mocked-get-path'
};
beforeEach(module('MyApp', function ($provide) {
$provide.constant('ROUTES', ROUTES);
}));
beforeEach(module('MyApp'));
beforeEach(inject(function(_$rootScope_, _$http_, _$timeout_, _MyFactory_, _$httpBackend_) {
$httpBackend = _$httpBackend_;
scope = _$rootScope_.$new();
$http = _$http_;
$timeout = _$timeout_;
MyFactory = _MyFactory_;
}));
it('should use ROUTES.get on method myGet', function(done) {
spyOn(Math, "random").and.returnValue(0.01);
$httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
MyFactory.myGet('elem1')
.then(function(res) {
expect($http.get).toHaveBeenCalledWith(ROUTES.get);
done();
});
$timeout.flush();
});
});
Это приведет к передаче неожиданной ошибки GET. Но тогда есть еще одна проблема, связанная с неполучением вызова в течение периода ожидания.
И, при условии, что теперь вы ожидаете правильного вызова в $ httpBackend, вы можете упростить свой тестовый пример, удалив его асинхронный характер следующим образом:
(удалите paremeter done и упростите тестовый код)
it('should use ROUTES.get on method myGet', function() {
spyOn(Math, "random").and.returnValue(0.01);
$httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
MyFactory.myGet('elem1');
$timeout.flush();
});
Комментарии:
1. Спасибо за ваш ответ, я попробовал то, что вы предложили, но это не сработало. Пожалуйста, проверьте эту модификацию, которую я написал с вашим предложением: plnkr.co/edit/b0hyazxaC5P3qKpLjqLm
2. Я приложил полный код, который устраняет неожиданную проблему с получением.
3. Еще раз спасибо. Теперь ошибка другая: «Ошибка: Тайм-аут — асинхронный обратный вызов не был вызван в течение времени ожидания, указанного jasmine. DEFAULT_TIMEOUT_INTERVAL.» Вот модификация: plnkr.co/edit/RwD3y9OmxSEywUhq2mUh
4. смотрите мою последнюю правку. ваши условия тестирования теперь проверяются ожиданиями в httpBackend, поэтому это можно выполнить с помощью вызовов синхронизации.
5. Еще раз спасибо, но почему результат Jasmine сообщает «СПЕЦИФИКАЦИЯ НЕ ИМЕЕТ ОЖИДАНИЙ, следует использовать ROUTES.get для метода MyGet»? Обновленный плунжер: plnkr.co/edit/RwD3y9OmxSEywUhq2mUh