Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sinon.js - stub React component's function before component is instantiated?

Let's say I have a component that looks like this:

var React = require('react/addons');

var ExampleComponent = React.createClass({
    test : function () {
        return true;
    },
    render : function () {
        var test = this.test();
        return (
            <div className="test-component">
                Test component - {test}
            </div>
        );
    }
});

module.exports = ExampleComponent;

In my test, I could render this component using TestUtils, then stub out the method like so:

var renderedComponent = TestUtils.renderIntoDocument(<ExampleComponent/>);
sinon.stub(renderedComponent, 'test').returns(false);
expect(renderedComponent.test).toBe(false); //passes

But is there a way I could tell Sinon to automatically stub out a component's function every time an instance of that component is created? Ex:

sinon.stubAll(ExampleComponent, 'test').returns(false); //something like this
var renderedComponent = TestUtils.renderIntoDocument(<ExampleComponent/>);
expect(renderedComponent.test).toBe(false); //I'd like this to pass

If this isn't possible, is there a potential solution that comes close to providing the functionality I'm looking for?

like image 416
Michael Parker Avatar asked Jun 18 '15 15:06

Michael Parker


2 Answers

You will need to overwrite the ExampleComponent.prototype instead of ExampleComponent. ExampleComponent is a constructor. Local methods like test() are saved in it's prototype.

sinon.stub(ExampleComponent.prototype, 'test').returns(false);
var renderedComponent = TestUtils.renderIntoDocument(<ExampleComponent/>);
expect(renderedComponent.test).toBe(false); //passes
like image 182
Victor Avatar answered Nov 05 '22 17:11

Victor


I found a solution to my problem.

To clarify, my problem is that I wanted to stub out functions that belong to children components that are rendered under a parent component. So something like this:

parent.js

var Child = require('./child.js');
var Parent = React.createClass({
    render : function () {
        return (
            <div className="parent">
                <Child/>
            </div>
        );
    }
});
module.exports = Parent;

child.js

var Child = React.createClass({
    test : function () {
        return true;
    },
    render : function () {
        if (this.test) {
            throw('boom');
        }
        return (
            <div className="child">
                Child
            </div>
        );
    }
});
module.exports = Child;

If I were to use TestUtils to render Parent in one of my tests, it would throw the error, which I wanted to avoid. So my problem was that I needed to stub out Child's test function before it was instantiated. Then, when I render Parent, Child won't blow up.

The answer provided did not quite work, as Parent uses require() to get Child's constructor. I'm not sure why, but because of that, I can't stub out Child's prototype in my test and expect the test to pass, like so:

var React = require('react/addons'),
    TestUtils = React.addons.TestUtils,
    Parent = require('./parent.js'),
    Child = require('./child.js'),
    sinon = require('sinon');

describe('Parent', function () {
    it('does not blow up when rendering', function () {
        sinon.stub(Child.prototype, 'test').returns(false);
        var parentInstance = TestUtils.renderIntoDocument(<Parent/>); //blows up
        expect(parentInstance).toBeTruthy();
    });
});

I was able to find a solution that fit my needs though. I switched my testing framework from Mocha to Jasmine, and I started using jasmine-react, which provided several benefits, including the ability to stub out a function of a class before it is instantiated. Here is an example of a working solution:

var React = require('react/addons'),
    Parent = require('./parent.js'),
    Child = require('./child.js'),
    jasmineReact = require('jasmine-react-helpers');

describe('Parent', function () {
    it('does not blow up when rendering', function () {
        jasmineReact.spyOnClass(Child, 'test').and.returnValue(false);
        var parentInstance = jasmineReact.render(<Parent/>, document.body); //does not blow up
        expect(parentInstance).toBeTruthy(); //passes
    });
});

I hope this helps someone else with a similar issue. If anyone has any questions I would be glad to help.

like image 24
Michael Parker Avatar answered Nov 05 '22 17:11

Michael Parker