sinon stub- cannot stub arrow function in a class

Created on 3 Sep 2016  ·  1Comment  ·  Source: sinonjs/sinon

  • Sinon version : v2.0.0-pre.2
  • Environment : Windows 10
  • Other libraries you are using: Typescript, babel, mocha, chai

What did you expect to happen?
I expected to be able to stub an arrow function in a class.

What actually happens
I can't stub an arrow function, however, I can stub the class prototype function.

FAILED TESTS:
  ExampleClass tests
    × should stub thisDoesntWork arrow function
      Chrome 52.0.2743 (Windows 10 0.0.0)
    TypeError: Attempted to wrap undefined property thisDoesntWork as function
        at wrapMethod (webpack:///~/sinon/pkg/sinon.js:3138:0 <- test-bundler.js:7377:21)
        at Object.stub (webpack:///~/sinon/pkg/sinon.js:2472:0 <- test-bundler.js:6711:12)
        at Context.<anonymous> (webpack:///src/stores/sinon.test.ts:22:51 <- test-bundler.js:96197:72)

How to reproduce

export class ExampleClass {
    thisWorks() {
        return 0;
    }

    thisDoesntWork = () => {
        return 0;
    }
}

describe("ExampleClass tests", () => {
    it("should stub thisWorks function", () => {
        let stubFunctionA = sinon.stub(ExampleClass.prototype, "thisWorks");
    });
    it("should stub thisDoesntWork arrow function", () => {
        let stubFunctionB = sinon.stub(ExampleClass, "thisDoesntWork");
    });
});
Documentation ES2015+

Most helpful comment

This is food for the upcoming new documentation site where we hope to publish some more tutorial oriented articles on stubbing, spying, etc in the context of ES2015.

Generally when you come across issues like this it is very helpful in remembering one simple fact: ES2015 is often just syntactic sugar on top of simple ES5 constructs. Meaning, when you don't understand why something happens (or doesn't happen), try deconstructing the code into its ES5 equivalent. The simplest way (which is what I just did) is to just paste the example code into the Babel playground and see what the ES2015/ES6 code compiles into:


The transpiled code from Babel

"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ExampleClass = exports.ExampleClass = function () {
    function ExampleClass() {
        _classCallCheck(this, ExampleClass);

        this.thisDoesntWork = function () {
            return 0;
        };
    }

    _createClass(ExampleClass, [{
        key: "thisWorks",
        value: function thisWorks() {
            return 0;
        }
    }]);

    return ExampleClass;
}();

The first thing I spot is that arrow functions are simply _instance methods_. That means a few things:

  1. they do not exist until object creation.
  2. they cannot be stubbed by modifying the prototype, since they do not exist on the prototype
  3. they cannot be stubbed by modifying the constructor object, since their definition is hidden within the closure.

Knowing all of this, it is simply to change your failing test to this:

    it("should stub thisDoesntWork arrow function", () => {
        var example = new ExampleClass();
        let stubFunctionB = sinon.stub(example, "thisDoesntWork", () => "actually it does work" );
    });

Good luck.

>All comments

This is food for the upcoming new documentation site where we hope to publish some more tutorial oriented articles on stubbing, spying, etc in the context of ES2015.

Generally when you come across issues like this it is very helpful in remembering one simple fact: ES2015 is often just syntactic sugar on top of simple ES5 constructs. Meaning, when you don't understand why something happens (or doesn't happen), try deconstructing the code into its ES5 equivalent. The simplest way (which is what I just did) is to just paste the example code into the Babel playground and see what the ES2015/ES6 code compiles into:


The transpiled code from Babel

"use strict";

Object.defineProperty(exports, "__esModule", {
    value: true
});

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ExampleClass = exports.ExampleClass = function () {
    function ExampleClass() {
        _classCallCheck(this, ExampleClass);

        this.thisDoesntWork = function () {
            return 0;
        };
    }

    _createClass(ExampleClass, [{
        key: "thisWorks",
        value: function thisWorks() {
            return 0;
        }
    }]);

    return ExampleClass;
}();

The first thing I spot is that arrow functions are simply _instance methods_. That means a few things:

  1. they do not exist until object creation.
  2. they cannot be stubbed by modifying the prototype, since they do not exist on the prototype
  3. they cannot be stubbed by modifying the constructor object, since their definition is hidden within the closure.

Knowing all of this, it is simply to change your failing test to this:

    it("should stub thisDoesntWork arrow function", () => {
        var example = new ExampleClass();
        let stubFunctionB = sinon.stub(example, "thisDoesntWork", () => "actually it does work" );
    });

Good luck.

Was this page helpful?
0 / 5 - 0 ratings