我有一个非常简单的函数需要测试:它返回一个对象并在运行时正常工作。
此类对象的大多数字段都是固定的,但其中一个字段根据函数的结果而变化:checkWeather.getWeather().
在单元测试中(Jasmine):checkWeather.getWeather()
监视spyOn(...).andReturn(FIXEDVALUE)
,以便它返回我想要的结果。但是当测试运行时,此函数返回'NOT_INITIALIZED',这意味着它没有被初始化。但是,既然我们有和返回,我们应该得到一个结果,并且根本不应该调用该函数;在测试期间,它应该由FIXEDVALUE替换。
请参阅代码中的注释,也许会更清楚。
您是否可以在测试代码中看到我的错误,以及为什么行为如此奇怪? (第一个文件工作正常,我需要编写单元测试,但这不起作用)。
function () {
'use strict';
angular
.module('weatherModule')
.service('TEST_ME', TEST_ME);
TEST_ME.$inject = ['FILTER_TYPE', 'checkWeather', 'values'];
function TEST_ME(FILTER_TYPE, checkWeather, values) {
// when this next is called by JASMINE, checkWeather.getWeather() returns 'NOT_INITIALIZED'
//but during testing I expect getWeather() to return VALUE as specified in .andReturn(VALUE) - see following file
console.log("checkWeather.getWeather(): ", checkWeather.getWeather());
return {
today: {
name: "today",
//Under Jasmine, the next condition will always be false
isSunny: checkWeather.getWeather() === values.SUNNY
}
};
}
})();
'use strict';
describe('TEST_ME', function () {
var TEST_ME;
var checkWeather;
var values;
beforeEach(angular.mock.module('weatherModule'));
beforeEach(inject(function ( _TEST_ME_, _checkWeather_, _values_) {
TEST_ME = _TEST_ME_;
checkWeather = _checkWeather_;
values = _values_;
}));
describe('Not fixed parts of TEST_ME', function () {
it('should sunny', function () {
var sunToday_TEST_ME = {
isSunny: checkWeather.getWeather() === values.SUNNY
}
};
spyOn(checkWeather, 'getWeather').andReturn(values.sunToday);
//The next works!! It prints the value values.sunToday set by .andReturn()
console.log("In UT, checkWeather.getWeather(): ", checkWeather.getWeather());
expect(TEST_ME.today.isSunny).toEqual(sunToday_TEST_ME.isSunny);
}); // This fails :(
});
通过编写单元测试,我失去了很多时间,远远超过编写整个功能!你能看出我的错误在哪里吗?
提前谢谢。
答案 0 :(得分:1)
*我已经将* TEST_ME **实现为工厂,只要AngularJS中的工厂可以返回任何内容,我们需要在此处关闭以存储注入服务checkWeather
的值。
在要测试的beforeEach
中,您将分配TEST_ME = _TEST_ME_;
函数TEST_ME接收相关值。那个地方是早期的,因为间谍还没有建立。所以,我决定从工厂返回函数并在测试中执行它。
(function() {
angular
.module('weatherModule', [])
.factory('TEST_ME', TEST_ME);
TEST_ME.$inject = ['FILTER_TYPE', 'checkWeather', 'values'];
function TEST_ME(FILTER_TYPE, checkWeather, values) {
return function() {
return {
today: {
name: "today",
isSunny: checkWeather.getWeather() === values.SUNNY
}
};
}
}
}());
describe('weatherModule', function() {
var TEST_ME, checkWeather, values
beforeEach(module('weatherModule'))
beforeEach(function() {
angular.module('weatherModule')
.value('FILTER_TYPE', '')
.value('checkWeather', {
getWeather: function() {}
})
.value('values', {
SUNNY: true
})
})
beforeEach(inject(function(_TEST_ME_, _checkWeather_, _values_) {
TEST_ME = _TEST_ME_;
checkWeather = _checkWeather_;
values = _values_;
}));
describe('The day is', function() {
it('sunny when `checkWeather.getWeather` return true', function() {
spyOn(checkWeather, 'getWeather').and.returnValue(true);
expect(TEST_ME().today.isSunny).toEqual(true);
});
it('ugly when `checkWeather.getWeather` return false', function() {
spyOn(checkWeather, 'getWeather').and.returnValue(false);
expect(TEST_ME().today.isSunny).toEqual(false);
});
});
})
<script src="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<link href="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
(function() {
angular
.module('weatherModule', [])
.service('TEST_ME', TEST_ME);
TEST_ME.$inject = ['FILTER_TYPE', 'checkWeather', 'values'];
function TEST_ME(FILTER_TYPE, checkWeather, values) {
this.today = {
name: "today",
isSunny: checkWeather.getWeather() === values.SUNNY
}
}
}());
describe('weatherModule', function() {
var TEST_ME, instatiateService, checkWeather, values
beforeEach(module('weatherModule'))
beforeEach(function() {
angular.module('weatherModule')
.value('FILTER_TYPE', '')
.value('checkWeather', {
getWeather: jasmine.createSpy('foo')
})
.value('values', {
SUNNY: true
})
})
beforeEach(inject(function(_checkWeather_, _values_, $injector) {
checkWeather = _checkWeather_;
values = _values_;
instatiateService = function() {
return $injector.get('TEST_ME')
}
}));
describe('The day is', function() {
it('sunny when `checkWeather.getWeather` returns true', function() {
checkWeather.getWeather.and.returnValue(true)
TEST_ME = instatiateService()
expect(TEST_ME.today.isSunny).toEqual(true);
});
it('ugly when `checkWeather.getWeather` return false', function() {
checkWeather.getWeather.and.returnValue(false)
TEST_ME = instatiateService()
expect(TEST_ME.today.isSunny).toEqual(false);
});
});
})
<script src="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<link href="https://safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>
答案 1 :(得分:0)
最后我改变了服务的架构以使其可测试。 由于某些原因,由于系统原因,在创建对象之前调用spyOn()。和Return()是不可能的(或非常困难的)。所以我添加了刷新值的可能性,它们将使用spied函数设置。
以前,TEST_ME非常容易使用,因为它足以以这种方式使用对象:TEST_ME.xxx。 现在,有必要做TEST_ME.getWeatherConditions()。xxx。
但是谁在乎,我改变了对TEST_ME.xxx的所有调用→TEST_ME.getWeatherConditions()。xxx 现在它更可测试了
function () {
'use strict';
angular
.module('weatherModule')
.service('TEST_ME', TEST_ME);
TEST_ME.$inject = ['FILTER_TYPE', 'checkWeather', 'values'];
function TEST_ME(FILTER_TYPE, checkWeather, values) {
var conditions = null;
//the rest of the project doesn't know that this need to be done, so do it during creation.
updateWeatherConditions(); //the rest of the project doesn't know that this need to be done, so do it during creation.
return {
updateWeatherConditions: updateWeatherConditions,
getWeatherConditions: getWeatherConditions
};
function getWeatherConditions() {
return conditions;
}
//Using this function i can update the result whenever I want
function updateWeatherConditions() {
conditions = {
today: {
name: "today",
//Every time we call getWeatherConditions, the return value is refreshed
isSunny: checkWeather.getWeather() === values.SUNNY
}
};
}
}
})();
'use strict';
describe('TEST_ME', function () {
var TEST_ME;
var checkWeather;
var values;
beforeEach(angular.mock.module('weatherModule'));
beforeEach(inject(function ( _TEST_ME_, _checkWeather_, _values_) {
TEST_ME = _TEST_ME_;
checkWeather = _checkWeather_;
values = _values_;
}));
describe('Not fixed parts of TEST_ME', function () {
it('should sunny', function () {
var sunToday_TEST_ME = {
isSunny: checkWeather.getWeather() === values.SUNNY
}
};
spyOn(checkWeather, 'getWeather').andReturn(values.sunToday);
// I don't care if conditions were created before spyOn() - i refresh them now
TEST_ME.updateWeatherConditions();
expect(TEST_ME.getWeatherConditions().today.isSunny).toEqual(sunToday_TEST_ME.isSunny);
});
});