Javascript 如何测试函数是否调用特定方法/函数?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26704677/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-22 23:15:08  来源:igfitidea点击:

How do I test if a function calls a specific method/function?

javascriptnode.jstddmochachai

提问by Hal Carleton

Is there a way in Mocha to test if a function calls a specific method or external function?

Mocha 中有没有办法测试一个函数是否调用了特定的方法或外部函数?

I am using Mocha with Chai, but am open to any other assertion libraries.

我正在将 Mocha 与 Chai 一起使用,但我对任何其他断言库持开放态度。



Ok, so testing whether a methid is being called is pretty easy using sinon. I'm not sure about testing to see if an external function is being called though. So I updated the examples to represent something a little more "real world". I am working on a node app, so foo.jsand bar.jsare both modules.

好的,所以使用 sinon 测试是否正在调用方法是很容易的。我不确定是否要测试以查看是否正在调用外部函数。所以我更新了示例以代表更“真实世界”的东西。我正在开发一个节点应用程序,所以foo.jsbar.js都是模块。

Example:

例子:

foo.js

foo.js

var bar = require('bar');
var xyz = function () {};

var Foo = module.exports = function () {
  this.bar();
  bar();
  xyz();
};
Foo.prototype.bar = function () {};

bar.js

bar.js

var bar = module.exports = function () {};

fooSpec.js

fooSpec.js

var chai      = require('chai');
var sinon     = require('sinon');
var sinonChai = require('sinonChai');
var expect    = chai.expect;
var Foo       = require('../lib/foo');

chai.use('sinonChai');

describe('Foo', function () {

  var method;

  beforeEach(function (done) {
    method = sinon.spy(Foo.prototype, 'bar');
    done();
  });
  afterEach(function (done) {
    method.restore();
    done();
  });

  it('should call Foo.prototype.bar() immediately', function () {

    new Foo();
    expect(method).to.have.been.called;

  });

  it('should call the module bar immediately', function () {
    // ????????????
  });

  it('should call xyz() immediately', function () {
    // ????????????
  });
});

So as you can see I've figured out how to test for Foo.prototype.bar, but I can't find a way to implement the second and third tests.

正如你所看到的,我已经找到了如何测试Foo.prototype.bar,但我找不到实现第二个和第三个测试的方法。

回答by Hal Carleton

So this question was really two in one.

所以这个问题实际上是二合一的。

Firstly, "how to test if a method is being called": I laid out the code for this in the example, but basically, using sinon.js you just wrap the method in a "spy" which allows you to write a test that expects that spy to have been called.

首先,“如何测试一个方法是否被调用”:我在示例中为此列出了代码,但基本上,使用 sinon.js 您只需将方法包装在“间谍”中,这样​​您就可以编写一个测试预计该间谍已被调用。

Secondly, "how to test if a private function(one that was not exported as part of the module) has been called":

其次,“如何测试私有函数(未作为模块的一部分导出的函数)是否已被调用”:

Basically, you don't. It is possible to export these functions when in a testing environment and not in production, but this seems a little too hacky to me.

基本上,你没有。在测试环境中而不是在生产环境中导出这些函数是可能的,但这对我来说似乎有点太hacky了。

I've come to the conclusion that when calling another module you should just break the TDD cycle and not test for this since it's probably going to be a small amount of code and the module will have already been tested on it's own.

我得出的结论是,当调用另一个模块时,您应该只是打破 TDD 循环而不是对此进行测试,因为它可能是少量代码,并且该模块已经过单独测试。

If you are calling a private function that is declared within you're module and want to test it you should write a more broad test that tests for the result of this function being called rather than testing whether the function is being called or what is actually happening within the function.

如果你正在调用一个在你的模块中声明的私有函数并想测试它,你应该编写一个更广泛的测试来测试这个函数被调用的结果,而不是测试函数是否被调用或实际上是什么函数内发生。

Here's a really simple example:

这是一个非常简单的例子:

foo.js

foo.js

var _ = require('lodash');

var Foo = module.exports = function (config) {

  this.config = _.merge({
      role: 'user',
      x: '123',
      y: '321'
    },
    config);

  this.config.role = validateRole(this.config.role);
};

var validateRole = function (role) {
  var roles = [
    'user', 'editor', 'admin'
  ];

  if (_.contains(roles, role)) {
    return role;
  } else {
    return 'user'
  }
};

fooSpec.js

fooSpec.js

var chai = require('chai');
var expect = chai.expect;
var Foo = require('../lib/foo');

describe('Foo', function () {

  it('should set role to \'user\' if role is not valid', function () {

    var foo = new Foo({role: 'invalid'});
    expect(foo.config.role).to.equal('user');

  });

};

回答by jpenna

I'm using expectassertion library with Mocha, but Chaimight have analogous methods

我正在使用expect断言库Mocha,但Chai可能有类似的方法



First

第一的

You can test if a function calls a specific method/function using Spies. You did this in you code above.

您可以使用 Spies 测试函数是否调用特定方法/函数。你在上面的代码中做到了这一点。

Second

第二

The problem with the code you are testing is Context. So I will address it in this answer. You can test if an external function is called, but it needs a context, so you might have to change your code.

您正在测试的代码的问题是上下文。所以我将在这个答案中解决它。您可以测试是否调用了外部函数,但它需要上下文,因此您可能需要更改代码。

I'm using bar(module) as example. For xyz(function) go to the Second method. The explanation is the same for both.

我以bar(module) 为例。对于xyz(函数)转到第二种方法。两者的解释是一样的。

1. Export barinside an object

1.bar在对象内部导出

bar.js

bar.js

var bar = module.exports = { 
  bar: function () {}; 
}

foo.js

foo.js

var Foo = module.exports = function () {
  bar.bar();
  ....
};

This way you can spy on it doing:

通过这种方式,您可以监视它:

fooSpec.js

fooSpec.js

it('should call the module bar immediately', function () {

  //note I'm getting the bar method from the exported object (bar module)
  var bar = expect.spyOn(bar, 'bar'); 

  new Foo();

  expect(bar).toHaveBeenCalled();

2. Set barmodule as Foo's prototype method

2.设置barmodule为Foo的原型方法

If you don't want to change bar.js, you may set the required module as a prototype method of Foo. Then you have a context to spy on.

如果不想更改bar.js,可以将所需模块设置为 Foo 的原型方法。然后你有一个可以监视的上下文。

foo.js

foo.js

var bar = require('./bar');

var Foo = module.exports = function () {
  this.bar();
  this.barModule();
};
Foo.prototype.bar = function () {};
Foo.prototype.barModule = bar; // setting here as barModule

fooSpec.js

fooSpec.js

it('should call the module bar immediately', function () {
  var barSpy = expect.spyOn(Foo.prototype, 'barModule');

  new Foo();

  expect(barSpy).toHaveBeenCalled();    
});


Explanation

解释

The changes you must do are for changing the context of your variables.

您必须做的更改是为了更改变量的上下文。

To make it clear:

要说清楚:

var bar = require('bar');

var Foo = module.exports = function () {
  this.bar();
  bar();
};
Foo.prototype.bar = function () {};

In this snippet, you are requiring barand later setting this.barusing Foo.prototype. So, how can you set 2 variables with the same name and reference each other nicely?

在这个片段中,你需要bar和以后的设置this.bar使用Foo.prototype。那么,如何设置两个同名的变量并很好地相互引用呢?

The answer is Context and Scope. Your this.baris referencing the barvariable set in thiscontext (which points to Foo). On the other hand, your bar- note there is no this- is referencing the barvariable set in the function's (module) scope.

答案是上下文和范围。您this.bar正在引用上下文中bar设置的变量this(指向Foo)。另一方面,您bar- 请注意没有this- 正在引用bar在函数(模块)范围内设置的变量。

So, you may test your Foo.prototype.bar, since it is a module method, has a context and you may spy on it. Buy you can't spy on the required barbecause it is scoped (think of it as private).

所以,你可以测试你的Foo.prototype.bar,因为它是一个模块方法,有一个上下文,你可以监视它。购买您无法监视所需的内容,bar因为它是有范围的(将其视为私有的)。

Good read: http://ryanmorr.com/understanding-scope-and-context-in-javascript/

好读:http: //ryanmorr.com/understanding-scope-and-context-in-javascript/