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?
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
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.
Related
I'm writing a Protractor test and in my test.step.js file I have
element(by.css('...')).getText().then(function (text) {
expect(text).to.equal('expectedText');
});
This works as expected and passes.
Instead I created a test.page.js file and in there put this.field = element(by.css('...')); and then in my step file had
"use strict"
module.exports = function exampleTest() {
var TestPage = require("...");
var testPage = new TestPage;
...
test.Then(..., function (next) {
testPage.field.getText().then(function (text) {
expect(text).to.equal('expectedText');
});
});
}
then field is undefined. I have also tried adding getText() in the page file, but again get undefined or get told that I can't call 'then' on undefined.
In my mind, this should do exactly the same thing as the first example, but I'm far from an expert with Angular or JavaScript.
test.page.js looks like:
"use strict";
module.exports = (function () {
function TestPage() {
this.field = element(by.css('...'));
}
return TestPage;
});
Hoping someone can shine some light on why this is happening and what I should do instead to be able to put the CSS selector inside a page file for re-use.
Thanks
Your code new TestPage; returns the constructor TestPage, but it's never called.
You could return the class :
function TestPage() {
this.field = element(by.css('...'));
}
module.exports = TestPage;
var TestPage = require("...");
var testPage = new TestPage;
testPage.field.getText().then(...
Or an instance of the class:
function TestPage() {
this.field = element(by.css('...'));
}
module.exports = new TestPage();
var testPage = require("...");
testPage.field.getText().then(...
The way you defined re-usable element locators looks different. I am following some thing like below
Step 1: Define a .js file which should contain the Locator objects and re-usable methods
var Login = {
PageElements: {
emailInput: element(by.css('#email')),
passwordInput: element(by.css('#password')),
loginForm: element(by.css('#form')),
},
doLogin: function doLogin() {
this.PageElements.emailInput.sendKeys('blahblah#email.com');
this.PageElements.passwordInput.sendKeys('blahblah');
this.PageElements.loginForm.submit();
},
};
module.exports = Login;
Step 2: Call these page objects in your test classes.
var LoginPage = require('../pageobjects/LoginPage.js');
it('Scenario1_Login',function(){
LoginPage.PageElements.emailInput.sendKeys('blahblah');
});
More details here
I have 2 components - addProjectForm and listProjects. They are both nested components inside the root module. Whenever I add a project using the form, I want it to appear in the list straight away.
To achieve this, I had to pass down the controller instance to each component like this:
var RootComponent = {};
rootComponent.controller = function() {
this.example = 'test variable';
}
rootComponent.view = function(ctrl) {
return [
m.component(addProjectForm, ctrl),
m.component(listProjects, ctrl)
];
}
and then the listProjectscomponent for example, looks like this:
var listProjects = {
controller: function(root) {
this.root = root;
},
view: function(ctrl) {
console.log(ctrl.root.example);
}
};
So this way I keep calling methods on the top level, but I don't quite like passing down the controller instance like this. Is there any other way I should be doing it?
I think this is what you're looking for:
Mithril.js: Should two child components talk to each other through their parent's controller?
A newer way of solving this common problem is to use a Flux like architecture developed by Facebook:
https://facebook.github.io/flux/
Writing your own dispatcher is semi-trivial. Here's an example that someone else built alongside Mithril:
https://gist.github.com/MattMcFarland/25fb4f0241530d2f421a
The downside with this approach is it would be somewhat anti-Flux to use m.withAttr, as views aren't supposed to write directly to models in the dispatcher paradigm.
The problem you have is the difference between passing by reference or by value. In JS all primitive types are passed by value. Thats why you can't pass the string directly since it's cloned during pass. You have multiple options here:
You can use m.prop and just pass the variable down to the components, m.props stores the value in function that is always passed by reference.
var RootComponent = {};
rootComponent.controller = function() {
this.example = m.prop('test variable');
}
rootComponent.view = function(ctrl) {
return [
m.component(addProjectForm, ctrl.example),
m.component(listProjects, ctrl.example)
];
}
If the variable is an array, it will be passed by reference anyways.
Second option is to keep the list in the root context and add a callback to the second component.
var RootComponent = {};
rootComponent.controller = function() {
var projects = this.projects = [];
this.addProject = function(project) {
projects.push(project);
}
}
rootComponent.view = function(ctrl) {
return [
m.component(addProjectForm, {
onsubmit: ctrl.addProject
}),
m.component(listProjects, ctrl.projects)
];
}
So I'm testing a component where I need it to act with my store to get the correct testable behavior. I've read that, since the "View" requires the store to function, I need to not mock the store, and also to not mock the "object-assign" thingy.
Excerpts from test code:
jest.dontMock('object-assign');
jest.dontMock('../StationSearch.jsx');
jest.dontMock('../../stores/StationStore.js');
describe('StationSearch', function() {
var StationSearch;
var stationSearch;
var React;
var TestUtils;
beforeEach(function() {
React = require('react/addons');
TestUtils = React.addons.TestUtils;
StationSearch = require('../StationSearch.jsx');
stationSearch = TestUtils.renderIntoDocument(<StationSearch />);
});
it('is rendered without value by default', function() {
// code here
}
);
And some code from the View:
var React = require('react');
var StationStore = require('../stores/StationStore');
var StationSearch = React.createClass({
componentDidMount: function() {
StationStore.addChangeListener(this._onChange);
},
});
Now, when running my tests, I get
TypeError: Cannot call method 'addChangeListener' of undefined
...on my change listener line, despite that I (think that I) am doing the right things when it comes to "not mocking" things.
Anyone has a clue as to why I could be getting this, still?
probably late but...
the store is undefined because object-assign is being mocked. unmock it with one of the following methods
1) add this line to the top of your jest test file
jest.dontMock('object-assign');
2) add this to your package.json file
"jest": {
"unmockedModulePathPatterns": [
"object-assign"
]
},
In my component's render method, I need to filter through the props.children variable looking for components that have a specific method or property, but I'm unable to expose any methods or properties on the children. I'd like to have the method be inherited from a mixin.
Example:
var barMixin = {
isBar: function() { return true; }
};
var Foo = React.createClass({
render: function() {
var filteredChildren = this.props.children.filter(function(child) {
return child.isBar();
});
return (
<div>
{filteredChildren}
</div>
);
}
});
Unfortunately, this example doesn't work because child.isBar is undefined even when the child components inherit the mixin barMixin.
Is there something I'm misunderstanding? I've also tried declaring my mixin methods as static like this:
var barMixin = {
statics: {
isBar: function() { return true; }
}
};
Any help would be greatly appreciated.
Edit:
I've found a working solution:
var barMixin = {
getDefaultProps: function() {
return {
isBar: function() { return true; }
};
}
};
Checking in the render function with: child.props.isBar(). However, this doesn't seem like the correct way to do this.
I've made a small library to deal with the Children structure that may be handy for your problem. You can deal even with deep children if you need. You can get it here: https://github.com/fernandopasik/react-children-utilities.
import React from 'react';
import Children from 'react-children-utilities';
function isBar(child) { return true; }
function Foo(props) {
return <div>{Children(this.props.children).filter(isBar)}</div>;
};
TL;DR If your problem is according to access the mixin from children, then don't read, i possibly misread your question :( Otherwise, some things about mixins lower...
You should stick to the original form of using a mixin. I think, the best use-case to use a mixin is code sharing. So i assume, you are using some module system in your code, to require the mixin. If so, you can use it like:
var Foo = React.createClass({
mixins: [fooMixin],
render: function() {
console.log(this.isBar()); // using the mixin
return (
<div>
Something
</div>
);
}
});
I've created a sample to demonstrate the usage.
define('fooMixin', {
isBar: function() {
return 'Something';
}
});
require(['fooMixin'], function(fooMixin) {
var Foo = React.createClass({
mixins: [fooMixin],
render: function() {
var div = React.createFactory('div');
return div(null, this.isBar());
}
});
React.render(Foo(), document.getElementById('content'));
});
<script src="http://fb.me/react-with-addons-0.12.0.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.15/require.min.js"></script>
<div id="content"></div>
Also the sample can be seen on JSFiddle with JSX -> http://jsfiddle.net/8hby56sa/
Hope it helps.
I'm trying to stub a React component method for testing purpose:
var Comp = React.createClass({
displayName: "Comp",
plop: function() {
console.log("plop");
},
render: function() {
this.plop();
return React.DOM.div(null, "foo");
}
});
var stub = sinon.stub(Comp.type.prototype, "plop");
React.addons.TestUtils.renderIntoDocument(Comp());
sinon.assert.called(stub); // throws
This sadly keeps printing "plop" onto the console… and the assertion fails.
Note: Directly stubbing the spec object method works, but then you have to export the component constructor and the spec separately so they're both available in tests… Also, you'd need to stub the spec before even creating the component class; not so convenient:
var CompSpec = {
displayName: "Comp",
plop: function() {
console.log("plop");
},
render: function() {
this.plop();
return React.DOM.div("foo");
}
};
var stub = sinon.stub(CompSpec, "plop");
var Comp = React.createClass(CompSpec);
React.addons.TestUtils.renderIntoDocument(Comp());
// plop() is properly stubbed, so you can
sinon.assert.called(stub); // pass
Can you think of another strategy to easily stub a React component method?
You're running up against React's auto-binding feature, which caches the .bind(this) which is wrapped around your class methods. You can get your code to work by stubbing the cached version of the method in React's __reactAutoBindMap:
var Comp = React.createClass({
displayName: "Comp",
plop: function() {
console.log("plop");
},
render: function() {
this.plop();
return React.DOM.div(null, "foo");
}
});
// with older versions of React, you may need to use
// Comp.type.prototype instead of Comp.prototype
var stub = sinon.stub(Comp.prototype.__reactAutoBindMap, "plop"); // <--
React.addons.TestUtils.renderIntoDocument(React.createElement(Comp));
sinon.assert.called(stub); // passes
Which test framework are you using?
If you use jasmine, I've found jasmine-react to be a useful library for spying on React methods as well as replacing Components with test stubs.
In this case, you can spy on your method easily outside component definition.
//Component Definition
var Comp = React.createClass({
displayName: "Comp",
plop: function() {
console.log("plop");
},
render: function() {
this.plop();
return React.DOM.div(null, "foo");
}
});
//test
it("should call plop method on render", function(){
//spy on method
jasmineReact.spyOnClass(Comp, "plop");
React.addons.TestUtils.renderIntoDocument(Comp());
expect(Comp.plop).toHaveBeenCalled();
})
jasmineReact.spyOnClass returns an ordinary jasmine spy that you can use to track calls to it and its arguments.
If you want to actually stub the method and make it return something, you can do something like
jasmineReact.spyOnClass(Comp, "plop").andReturn('something')
Alternatively Facebook have recently launched a test framework Jest (also has jasmine as a dependency) which they use themselves for testing React components. Component methods can easily be stubbed using this framework. This looks like its worth checking out as well, but probably comes into its own a bit more when you write your components inside commonJS modules