Jasmine:调用一个函数来替换为andReturn()

时间:2016-11-28 15:34:40

标签: javascript angularjs unit-testing jasmine karma-jasmine

我有一个非常简单的函数需要测试:它返回一个对象并在运行时正常工作。 此类对象的大多数字段都是固定的,但其中一个字段根据函数的结果而变化:checkWeather.getWeather().

在单元测试中(Jasmine):checkWeather.getWeather()监视spyOn(...).andReturn(FIXEDVALUE),以便它返回我想要的结果。但是当测试运行时,此函数返回'NOT_INITIALIZED',这意味着它没有被初始化。但是,既然我们有和返回,我们应该得到一个结果,并且根本不应该调用该函数;在测试期间,它应该由FIXEDVALUE替换。

请参阅代码中的注释,也许会更清楚。

您是否可以在测试代码中看到我的错误,以及为什么行为如此奇怪? (第一个文件工作正常,我需要编写单元测试,但这不起作用)。

weather.js(这在运行时正常工作)

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
            }
        };
    }
})();

耐候TEST.js

'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 :(
});

通过编写单元测试,我失去了很多时间,远远超过编写整个功能!你能看出我的错误在哪里吗?

提前谢谢。

2 个答案:

答案 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 现在它更可测试了

weather.js

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
            }
        };
        }
    }
})();

耐候TEST.js

'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);
    });
});