I am just starting with Jest and Snapshot testing and I was wondering why all examples do "deep rendering" of React components to create snapshots.
Example
const A = () => {
return <div><B /><B /></div>
}
const B = () => {
return <div>This is B</div>
}
// TEST
describe('Test', () => {
it('renders correctly', () => {
const tree = ReactTestRenderer.create(
<A />
).toJSON();
expect(tree).toMatchSnapshot();
});
});
Snapshot:
exports[`Summary DOM rendering renders correctly 1`] = `
<div>
<div>
This is B
</div>
<div>
This is B
</div>
</div>
`;
While this is useful sometimes I think it makes far more sense to have separate tests/snapshots for A and B and to do shallow rendering so if I change B my A snapshots do not need to be updated. So I want my snapshots to look like this:
exports[`Summary DOM rendering renders correctly 1`] = `
<div>
<B />
<B />
</div>
`;
Is there any way to do this? Is this a good idea in the first place? If it is possible why is shallow rendering not the preferred way in the docs?
Update(Jan 3, 2018)
Shallowrender has been moved to react-test-renderer
import ShallowRenderer from 'react-test-renderer/shallow'
it('Matches snapshot', () => {
const renderer = new ShallowRenderer()
const result = renderer.render(<A />)
expect(result).toMatchSnapshot()
})
You can use react-test-utils Shallow Rendering with snapshot testing as well:
import ReactTestUtils from 'react-addons-test-utils';
describe('Test', () => {
it('renders correctly', () => {
const renderer = ReactTestUtils.createRenderer();
expect(renderer.render(<A />)).toMatchSnapshot();
});
});
With that you can create renderer that only renders 1 level deep, that is: it'll only render what's in your component's render() function, and not render child components.
react-test-renderer is a different renderer, it renders your component (and the whole tree) to JSON. Currently it has no option to shallow render, it will work just like in the browser and render everything, but to JSON.
They both are good for testing because they don't require a DOM environment and they have different characteristics. You can choose one that suits better your use case.
You can use enzyme to shallow-render your components.
I can't tell you for sure as to why it's not the preferred method in the docs, but my guess would be that it's because the functionality isn't built into the official react-test-renderer.
Shallow rendering is preferred for unit tests, where only a single component is being tested. In your question, shallow rendering the <A/> component is the correct approach. The it('renders correctly') test should only check that <A/> renders a certain way. That test should not depend on how <B/> renders.
To test more complex behaviors that involve multiple components, mount or render can be used. This allows for testing aspects of <A/> and <B/>.
(I use enzyme for test rendering.)
Related
I'm having trouble with unit testing using mocha, chai and enzyme for the following. I can't seem to understand how to unit test methods in components and how to unit test onClick methods will call those methods.
The following is what I am trying to unit test:
<Link to="/create-new-template-results" onClick={this.checkLink}>
<Button
buttonname="Next_button"
variant="primary"
label="Save"
onClickMethod={() => this.submitTemplateCreation()}
disabled={!this.disabledButtonCheck()}
/>
</Link>
.
Header: '',
Cell: value => {
return (
<div>
<img
height={34}
src="https://content.usaa.com/mcontent/static_assets/Media/icon-trash.svg"
onClick={() => this.removeAttribute(value)}
/>
</div>
);
}
.
removeAttribute = value => {
this.props.change('templateAttributeForm', value.original.name, '');
this.props.removeAttributeItem(value.index);
};
submitTemplateCreation() {
let profLvlData = Object.values(this.props.templateAttributeFormData);
let attrData = Object.keys(this.props.templateAttributeFormData);
let attributeProfLvl = attributeProfLvlUtil(attrData, profLvlData);
let templateCreationJSON = templateCreationPOSTFilter(attributeProfLvl, this.props.templateFormData);
this.props.submitTemplateCreation(templateCreationJSON);
}
Chai provides some nice tools for testing things exactly like what you are talking about.
You'll want to render your component somehow in the virtual DOM, either by using enzyme's "shallow" or "mount" functions.
Once you've done that, you can access the component using .find, and "simulate" an event using .simulate like so.
wrapper.find('Button').at(0).simulate('click');
This will find all of the 'Button' components in your wrapper, take the first one, and simulate a click. From there you can use expect() combined with any of the ways Chai provides to examine the state of the component in order to test that your button did what it was supposed to.
Since it seems like you are particularly interesting in the calling of the onClick function itself, I will add that you can specifically check to see if a function is called by doing the following with Chai.
expect(MyComponent.prototype.myOnClickFunction).to.have.property('callCount', 1);
Is there a better solution to dive into props.text in Enzyme?
Component:
export function TitleText ({ text, info, required }) {
return (
<div className={style.titleText}>
<div className={style.titleText} style={{ margin: 0 }} required={required}>{text}</div>
{info ? <InfoIcon className={style.infoIcon} /> : ''}
</div>
)
}
Test:
it('renders text from its props', () => {
const wrapper = setupTitleText('Test')
expect(wrapper.find(`.${style.titleText}`).node.props.children[0].props.children).toEqual('Test')
})
You can access the props using props(). Should at least work on shallow and mounted components.
wrapper.props().text
However, I would add a data attribute for testing.
Doing so would
let anyone working on your code would know that this element is being tested
let anyone working on your code would know that this element is being tested
prevent multiple elements being returned when you're looking to test something specific
prevent others breaking your tests if they decide to change the class.
I am currently Jest to test React component, component's inline style would be changed according to different props value.
this is an example about what I wanna do:
let firstChild = TestUtils.findRenderedDOMComponentWithTag(renderedComponent, 'div');
expect(firstChild.getDOMNode().style).toEqual({
fontSize: '20px'
});
This is the component props:
let renderedComponent = TestUtils.renderIntoDocument(
<CircleIcon
size="small" />
And this is the component dom to test with:
return (
<div className="circle-icon" style={boxStyle}>
<span className={this.props.icon}></span>
</div>
);
If I can get what is inside boxStyle, I can assert the test result from it.
Thanks a lot!
actually it works.
usage:
firstChild.getDOMNode().style.backgroundColor
I am using ember-cli qunit testing using moduleForComponent. I have the following select element inside an ember component I have created.
{{input site as="select"
collection="sites"
selection="site"
optionLabelPath="content.siteLabel"
optionValuePath="content.id"
prompt="Please Select"
label=" "
}}
The actual sites collection is found using the store.
sites : function() {
return this.get('store').find('site');
}.property()
I am using jsMockito to mock out the store.
var siteMock = mock(DS.Model);
when(siteMock).get('id').thenReturn(1);
when(siteMock).get('siteLabel').thenReturn('Qunit');
var storeMock = mock(DS.Store);
when(storeMock).find('site').thenReturn([siteMock]);
I pass this into the components as a parameter in the test.
var component = this.subject({
store : storeMock
});
The generated html looks like this, it seems that siteMock has rendered but the optionLabelPath and optionValuPath did not work correctly even though I have added the appropriate expectations on the mock.
<select id="ember473" class="ember-view ember-select">
<option value="">Please Select</option>
<option id="ember491" class="ember-view" value=""></option>
</select>
I have tested using the getters on the siteMock in the debugger and everything is working as expected. I guess I need another when condition on some property of siteMock but I am not sure what. Can anyone give me some advice on getting this working?
The problem seems to be that you are using content. in your paths optionLabelPath="content.siteLabel" which is thinking about controller proxing to a model.
But your test are using the models directly -undecorated by a controller- and they do not have a content property.
Im just starting on polymer. Im trying to unit test a custom element that has dependencies and I would like to fake/mock these out.
I've found Scott Miles recommendation on how to mock the core-ajax implementation. I thought I could follow that pattern easily but this only works as long as my element does not import the about to be mocked (core-ajax in this case) element.
If it does import it, then when the test tries to run I get
'Uncaught NotSupportedError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'core-ajax'. A type with that name is already registered.'
If I could do something like document.unregister the core-ajax element and import it again in my test, Id be a much happier dev!!!
Polymer is awesome but if I can not unit test it, then it presents major risks (at least when building an app that will need to be maintained/changed)
How are you guys working around this? I've been digging into Polymer and PolymerLab elements repo and most of them lack tests. So far I;ve not found much reference on how to do it.
Thanks for the help!
Santiago
Scotts' recommendation was:
Instead of importing core-ajax/core-ajax.html, create your own core-ajax element.
<polymer-element name="core-ajax" attributes="response">
<script>
Polymer('core-ajax', {
attached: function() {
this.response = ['a', 'b', 'c'];
}
});
</script>
</polymer-element>
Obviously, this is just an example, the actual implementation depends on the desired mocking behavior.
This is just one way to solve it, there are many others. I'm interested to hear what you find (in)convenient.
This question is a little old. Figured I'd provide an update since this is a pretty common situation.
Polymer CLI is the recommended approach for unit testing Polymer elements. The underlying library that it uses for testing is called web-component-tester (WCT). WCT has support for stub elements. Basically, if one of your tests relies on another element to return data, you can create a stub of that element that always returns consistent data.
JS in the unit test code for specifying the stub element:
setup(function() {
replace('paper-button').with('fake-paper-button');
});
Element to be tested:
<dom-module id='x-el'>
<template>
<paper-button id="pb">button</paper-button>
</template>
</dom-module>
At test runtime, the content template would be stamped out as:
<dom-module id='x-el'>
<template>
<fake-paper-button id="pb">button</fake-paper-button>
</template>
</dom-module>
https://www.polymer-project.org/1.0/docs/tools/tests#create-stub-elements
You can try registering it imperatively with js or extend every single element you are testing and override its properties or methods you want to mock.
i think that is just about it. It's like my google-map custom element, i import the google-map and change stuff around like so:
<polymer-element name="core-gmaps" attributes="lat long mapzoom markerlat markerlong markertitle" extends="google-map">
<template>
<style>
:host{
width: 100%;
}
#vivaMap {
display: block;
height: 100%;
width: 100%;
}
</style>
<google-map id="vivaMap" latitude="0" longitude="0" zoom="18">
<google-map-marker id="vivaMarker" title="" latitude="0" longitude=""></google-map-marker>
</google-map>
</template>
<script>
Polymer("core-gmaps",{
ready: function(){
var map = this.$.vivaMap;
map.latitude = Number(this.getAttribute('lat'));
map.longitude = Number(this.getAttribute('long'));
map.zoom = Number(this.getAttribute('mapzoom'));
var mapMarker = this.$.vivaMarker;
mapMarker.latitude = Number(this.getAttribute('markerlat'));
mapMarker.longitude = Number(this.getAttribute('markerlong'));
mapMarker.title = this.getAttribute('markertitle');
/*map.addEventListener('google-map-ready', function(e) {
console.log('Map loaded!');
});*/
}
});
</script>
</polymer-element>
I am still not sure if it was worth it professionally (i may end up not using it), but was totally worth it intellectually. learned some nice stuff. since i'm extending google-map it gets registered once and only once.
EDIT:
in my case i used the ready event because i couldn't manipulate the map per se without it being at least ready. but you can choose the event callback from the lifecycle methods. The list is here.
PS.:Yes, i didn't use data binding because i couldn't. The google map api was complaining about it being NaN so i had to cast it.