【问题标题】:AngularJS doesn't consider mocked service in jasmine testsAngularJS 在 jasmine 测试中不考虑模拟服务
【发布时间】:2018-08-09 10:42:17
【问题描述】:

我正在尝试使用 Jasmine 和 Karma 对 AngularJS 服务进行单元测试。以下是我的代码的最小(混淆)表示:

angular.module('app', []);
angular.module('app.mock', []);

var MyHelper = function(pollHelper) {
  var poller = pollHelper.getInstance();
};

var PollHelper = function() {
  var service = this;
  service.getInstance = function() {}
};

angular.module('app')
  .service('myHelper', MyHelper)
  .service('pollHelper', PollHelper);

angular.module('app.mock')
  .service('pollHelperMock', function() {
    var methods = ['getInstance'],
      pollHelper = jasmine.createSpyObj('pollHelperMock', methods);
    return pollHelper;
  });

describe('MyHelper', function() {
  var pollHelper, myHelper, $rootScope;
  pollHelper = jasmine.createSpy('pollHelper');
  beforeEach(module('app.mock'));

  beforeEach(module('app', function($provide) {
    console.log('prints 1'); // Prints on console

    $provide.service('pollHelper', function() {
      return pollHelper;
    });
  }));

  beforeEach(
    inject(
      function(_pollHelperMock_, _myHelper_, _$rootScope_) {
        console.log('prints 2'); // Doesn't print anything

        pollHelper.and.callFake(_pollHelperMock_);
        angular.extend(pollHelper, _pollHelperMock_);
        myHelper = _myHelper_;
        $rootScope = _$rootScope_;
      }));

  describe('Initialization', function() {
    it('Gets poller instance', function() {
      $rootScope.$apply();
    });
  });
});


/// Code for Jasmine test executor
(function() {
  var env = jasmine.getEnv();
  env.addReporter(new jasmine.HtmlReporter());
  env.execute();
}());
#HTMLReporter #jasmine_content,#TrivialReporter #jasmine_content{position:fixed;right:100%}body{background-color:#eee;padding:0;margin:5px;overflow-y:scroll}#HTMLReporter{font-size:11px;font-family:Monaco,"Lucida Console",monospace;line-height:14px;color:#333}#HTMLReporter a{text-decoration:none}#HTMLReporter a:hover{text-decoration:underline}#HTMLReporter h1,#HTMLReporter h2,#HTMLReporter h3,#HTMLReporter h4,#HTMLReporter h5,#HTMLReporter h6,#HTMLReporter p{margin:0;line-height:14px}#HTMLReporter .alert .bar,#HTMLReporter .banner,#HTMLReporter .resultMessage,#HTMLReporter .specDetail .description,#HTMLReporter .stackTrace,#HTMLReporter .summary,#HTMLReporter .symbolSummary{padding-left:9px;padding-right:9px}#HTMLReporter .version{color:#aaa}#HTMLReporter .banner{margin-top:14px}#HTMLReporter .duration{color:#aaa;float:right}#HTMLReporter .symbolSummary{overflow:hidden;margin:14px 0}#HTMLReporter .symbolSummary li{display:block;float:left;height:7px;width:14px;margin-bottom:7px;font-size:16px}#HTMLReporter .symbolSummary li.passed,#HTMLReporter .symbolSummary li.skipped{font-size:14px}#HTMLReporter .symbolSummary li.passed:before{color:#5e7d00;content:"\02022"}#HTMLReporter .symbolSummary li.failed{line-height:9px}#HTMLReporter .symbolSummary li.failed:before{color:#b03911;content:"x";font-weight:700;margin-left:-1px}#HTMLReporter .symbolSummary li.skipped:before{color:#bababa;content:"\02022"}#HTMLReporter .symbolSummary li.pending{line-height:11px}#HTMLReporter .symbolSummary li.pending:before{color:#aaa;content:"-"}#HTMLReporter .exceptions{color:#fff;float:right;margin-top:5px;margin-right:5px}#HTMLReporter .results,#HTMLReporter .summary{margin-top:14px}#HTMLReporter .bar{line-height:28px;font-size:14px;display:block;color:#eee}#HTMLReporter #details,#HTMLReporter.showDetails .summary{display:none}#HTMLReporter .runningAlert{background-color:#666}#HTMLReporter .skippedAlert{background-color:#aaa}#HTMLReporter .skippedAlert:first-child{background-color:#333}#HTMLReporter .skippedAlert:hover{color:#fff;text-decoration:underline}#HTMLReporter .passingAlert{background-color:#a6b779}#HTMLReporter .passingAlert:first-child{background-color:#5e7d00}#HTMLReporter .failingAlert{background-color:#cf867e}#HTMLReporter .failingAlert:first-child{background-color:#b03911}#HTMLReporter .resultsMenu,#HTMLReporter .resultsMenu a{background-color:#fff;color:#333}#HTMLReporter.showDetails .summaryMenuItem{font-weight:400;text-decoration:inherit}#HTMLReporter .summaryMenuItem,#HTMLReporter.showDetails .detailsMenuItem{font-weight:700;text-decoration:underline}#HTMLReporter.showDetails .summaryMenuItem:hover{text-decoration:underline}#HTMLReporter .resultMessage span.result,#HTMLReporter.showDetails #details{display:block}#HTMLReporter .summary .specSummary,#HTMLReporter .summary .suite .suite{margin-left:14px}#HTMLReporter .summary .specSummary.passed a{color:#5e7d00}#HTMLReporter .summary .specSummary.failed a{color:#b03911}#HTMLReporter .description+.suite{margin-top:0}#HTMLReporter .suite{margin-top:14px}#HTMLReporter .suite a{color:#333}#HTMLReporter #details .specDetail{margin-bottom:28px}#HTMLReporter #details .specDetail .description{display:block;color:#fff;background-color:#b03911}#HTMLReporter .resultMessage{padding-top:14px;color:#333}#HTMLReporter .stackTrace{margin:5px 0 0;max-height:224px;overflow:auto;line-height:18px;color:#666;border:1px solid #ddd;background:#fff;white-space:pre}#TrivialReporter{padding:8px 13px;position:absolute;top:0;bottom:0;left:0;right:0;overflow-y:scroll;background-color:#fff;font-family:"Helvetica Neue Light","Lucida Grande",Calibri,Arial,sans-serif}#TrivialReporter a,#TrivialReporter a:visited{color:#303}#TrivialReporter a:active,#TrivialReporter a:hover{color:#00f}#TrivialReporter .run_spec{float:right;padding-right:5px;font-size:.8em;text-decoration:none}#TrivialReporter .banner{color:#303;background-color:#fef;padding:5px}#TrivialReporter .logo{float:left;font-size:1.1em;padding-left:5px}#TrivialReporter .logo .version{font-size:.6em;padding-left:1em}#TrivialReporter .runner.running{background-color:#ff0}#TrivialReporter .options{text-align:right;font-size:.8em}#TrivialReporter .suite{border:1px outset gray;margin:5px 0;padding-left:1em}#TrivialReporter .suite .suite{margin:5px}#TrivialReporter .suite.passed{background-color:#dfd}#TrivialReporter .suite.failed{background-color:#fdd}#TrivialReporter .spec{margin:5px;padding-left:1em;clear:both}#TrivialReporter .spec.failed,#TrivialReporter .spec.passed,#TrivialReporter .spec.skipped{padding-bottom:5px;border:1px solid gray}#TrivialReporter .spec.failed{background-color:#fbb;border-color:red}#TrivialReporter .spec.passed{background-color:#bfb;border-color:green}#TrivialReporter .spec.skipped{background-color:#bbb}#TrivialReporter .messages{border-left:1px dashed gray;padding-left:1em;padding-right:1em}#TrivialReporter .passed{background-color:#cfc;display:none}#TrivialReporter .failed{background-color:#fbb}#TrivialReporter .skipped{color:#777;background-color:#eee;display:none}#TrivialReporter .resultMessage span.result{display:block;line-height:2em;color:#000}#TrivialReporter .resultMessage .mismatch{color:#000}#TrivialReporter .stackTrace{white-space:pre;font-size:.8em;margin-left:10px;max-height:5em;overflow:auto;border:1px inset red;padding:1em;background:#eef}#TrivialReporter .finished-at{padding-left:1em;font-size:.6em}#TrivialReporter.show-passed .passed,#TrivialReporter.show-skipped .skipped{display:block}#TrivialReporter .runner{border:1px solid gray;display:block;margin:5px 0;padding:2px 0 2px 10px}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine.js"></script>
<script src="https://cdn.jsdelivr.net/jasmine/1.3.1/jasmine-html.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-mocks.js"></script>

问题是,一旦我尝试执行my-helper.spec.js,它就会抛出一个错误,本质上说:

TypeError: pollHelper.getInstance is not a function

此外,只有第一个console.log 语句被打印,而不是第二个,这给人的印象是inject 函数根本没有被调用。

如何测试服务,在模块初始化后立即调用getInstance()

感谢您的帮助。

【问题讨论】:

  • 为什么有几个beforeEach?我不熟悉那个符号,所以首先我建议使用beforeEach(function() {/* contents from three different beforeEach's here */});
  • @jsruok -- 刚刚尝试将它们全部组合起来,仍然没有运气。
  • 这很奇怪。要模拟某些服务,您只需要: $provide.factory('$mdDialog', () => myMock);当你没有模拟时,你使用 callFake。
  • 当您使用混淆代码时,它可能会丢失一些细节,比如一些依赖项。此人需要第二双眼睛才能找到缺失的依赖项:stackoverflow.com/questions/41896782/…
  • @PetrAveryanov -- 问题是pollHelper.getInstance() 这个语句失败了,因为AngularJS 在加载模块后就试图实例化myService。我的问题是我使用哪种方法,以便调用模拟依赖项的getInstance()

标签: angularjs unit-testing jasmine mocking


【解决方案1】:

这是我们要测试的:

angular.module('app', []);

var MyHelper = function(pollHelper) {
  var poller = pollHelper.getInstance();
  console.log('in my helper:' + poller);
};

var PollHelper = function() {
  var service = this;
  service.name = 'original helper';
  service.getInstance = function() { return 'original helper instance'}
};

angular.module('app')
  .service('myHelper', MyHelper)
  .service('pollHelper', PollHelper);

测试中:

const pollHelperMock = {
  name: 'mock helper',
  getInstance: function() { return 'mock helper result'; }
}
beforeEach(module('app', function($provide) {
  $provide.service('pollHelper', function() {
     return pollHelperMock;
  });
}));

beforeEach(inject(function(pollHelper, myHelper) {
  console.log(pollHelper.name);
  console.log(pollHelper.getInstance());
}))

输出:

in my helper:mock helper result
mock helper
mock helper result

【讨论】:

  • 谢谢!您提供的解决方案在测试文件本身中为pollHelper 创建了模拟,这在某种程度上解决了问题,但我宁愿选择在不同的文件中创建它并在此处使用它。我想等待更多人发布答案,以帮助我了解解决此问题的另一种方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-07
  • 2013-06-09
  • 2015-07-07
相关资源
最近更新 更多