【问题标题】:Jasmine: a function is called instead to be replaced by andReturn()Jasmine:一个函数被调用而不是被 andReturn() 替换
【发布时间】:2016-11-28 15:34:39
【问题描述】:

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

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

请看代码中的cmets,也许会更清楚。

你能在测试代码中看到我的错误吗,为什么行为如此奇怪? (第一个文件完美运行,只是我需要编写单元测试,但这是行不通的)。


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

我写这个单元测试比写整个功能浪费了很多时间!你能看到我的错误在哪里吗?

提前谢谢你。

【问题讨论】:

  • 我不太明白 checkWeather 到底是什么...在我看来,您需要一个具有 getWeather 功能的 WeatherService 之类的东西。从您提供的代码中,函数 getWeather 没有在任何地方定义,所以它总是未定义的。也许您可以分享更多代码以更好地了解您到底要测试什么
  • 你真的需要两个beforeEach()。你打算使用beforeAll()吗?
  • 你好,checkWeather 是一项服务,它是注入的,但也许我没有以正确的方式进行操作……无论如何,似乎“unkonwn”是此类服务可以提供的值之一返回;就像 callFake 被忽略一样。我会继续调查,也等待答案。

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


【解决方案1】:

作为工厂

*我已经将 *TEST_ME** 实现为 factory,只要 AngularJS 中的 factory 可以返回任何内容,并且我们需要在此处关闭以存储注入服务 checkWeather 的值。

beforeEach under test 中,您将分配 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>

【讨论】:

  • 感谢我实施了第二个解决方案。好处是它起作用了,坏处是调用 getWeather 的其他测试现在都失败了。就像以这种方式监视的函数一样,对所有测试都进行了更改,而不仅仅是当前测试...
  • 您可以将间谍创建从 beforeEach 移动到 it。在 beforeEach 中将函数分配给getWeather,而不是在测试中向它添加 spy。
【解决方案2】:

最后,我更改了服务的架构以使其可测试。 由于系统的某些原因,在创建对象之前调用 spyOn().andReturn() 是不可能的(或非常困难的)。所以我添加了刷新值的可能性,它们将使用 spied 函数进行设置。

以前,TEST_ME 使用起来非常舒服,因为这样使用对象:TEST_ME.xxx 就足够了。 现在,有必要做 TEST_ME.getWeatherConditions().xxx。

但谁在乎呢,我把所有的调用都改成了 TEST_ME.xxx → TEST_ME.getWeatherConditions().xxx 现在它更容易测试了


天气.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);
    });
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    相关资源
    最近更新 更多