prior to posting this question, I tried to search in sqa stackexchange but I found no post about shallow and render there, so I hope someone can help me out here.
When should I use shallow and render in testing react components?
Based on the airbnb docs, I've made some opinions on the difference of the two:
Since shallow is testing components as a unit, so it should be used for 'parent' components. (ex. Tables, Wrappers, etc.)
Render is for child components.
The reason I asked this question, is that I'm having a hard time to figure out which one I should use (though the docs say that they're very similar)
So, how do I know which one to use in a specific scenario?
As per the Enzyme docs:
mount(<Component />) for Full DOM rendering is ideal for use cases where you have components that may interact with DOM apis, or may require the full lifecycle in order to fully test the component (ie, componentDidMount etc.)
vs.
shallow(<Component />) for Shallow rendering is useful to constrain yourself to testing a component as a unit, and to ensure that your tests aren't indirectly asserting on behavior of child components.
vs.
render which is used to render react components to static HTML and analyze the resulting HTML structure.
You can still see the underlying "nodes" in a shallow render, so for example, you can do something like this (slightly contrived) example using AVA as the spec runner:
let wrapper = shallow(<TagBox />);
const props = {
toggleValue: sinon.spy()
};
test('it should render two top level nodes', t => {
t.is(wrapper.children().length, 2);
});
test('it should safely set all props and still render two nodes', t => {
wrapper.setProps({...props});
t.is(wrapper.children().length, 2);
});
test('it should call toggleValue when an x class is clicked', t => {
wrapper.setProps({...props});
wrapper.find('.x').last().simulate('click');
t.true(props.toggleValue.calledWith(3));
});
Notice that rendering, setting props and finding selectors and even synthetic events are all supported by shallow rendering, so most times you can just use that.
But, you won't be able to get the full lifecycle of the component, so if you expect things to happen in componentDidMount, you should use mount(<Component />);
This test uses Sinon to spy on the component's componentDidMount
test.only('mount calls componentDidMount', t => {
class Test extends Component {
constructor (props) {
super(props);
}
componentDidMount() {
console.log('componentDidMount!');
}
render () {
return (
<div />
);
}
};
const componentDidMount = sinon.spy(Test.prototype, 'componentDidMount');
const wrapper = mount(<Test />);
t.true(componentDidMount.calledOnce);
componentDidMount.restore();
});
The above will not pass with shallow rendering or render
render will provide you with the html only, so you can still do stuff like this:
test.only('render works', t => {
// insert Test component here...
const rendered = render(<Test />);
const len = rendered.find('div').length;
t.is(len, 1);
});
The difference between shallow() and mount() is that
shallow() tests components in isolation from the child components they render while mount()goes deeper and tests a component's children.
For shallow() this means that if the parent component renders another component that fails to render, then a shallow() rendering on the parent will still pass.
Related
I'm trying to test my tooltip component, but it seems it does not exist :cry:
My .html
<div>
<boxComponent>
Some text
<tooltipComponent
#mouseover.native="handleHover(true)"
#mouseleave.native="handleHover(false)"
>This text appears on Hover</tooltipComponent>
</boxComponent>
<switchComponent button-type="button" :label="false" #change="activeFun" />
</div>
My .js
methods: {
handleHover (s) {
this.onHoverTooltip = s
},
}
My .spec.js
const localVue = createLocalVue()
localVue.use(Vuex)
//...
it('should reveal tooltip\'s mesage', () => {
const wrapper = shallowMount(ozFilters, {
propsData: {
//others stuffs,
label: false,
},
localVue,
store,
stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'],
})
expect(wrapper.find('tooltipComponent-stub').exists()).toBeFalsy()
// wrapper.vm.label = true
wrapper.vm.handleHover(true)
expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy()
})
I need to understand what should I do to test the tooltip component that is already a custom component.
Even without the -stub it does not work.
The error is occurring in this line expect(wrapper.find('tooltipComponent-stub').exists()).toBeTruthy() with says that the expect is false.
Well, there are a couple of things that need to be fixed/clarified.
First of all, you are using shallowMount to create a component which you want to test, it stubs all custom components provided in tested component so you don't have to additionally use stub parameter to stub them, you can easily delete this: stubs: ['tooltipComponent', 'boxComponent', 'switchComponent'].
When you want to find specific component it's recommended to use findComponent instead of just find. (Using find to search for a component is actually deprecated). To properly identify component it's best to import it in your test file and use it in findComponent argument, something like this:
import BoxComponent from 'path/to/BoxComponent'
it('lovely test', () => {
wrapper.findComponent(BoxComponent)
})
After this your test should pass, but there are some things to consider like using mount instead of shallowMount. It's explained here, but to wrap it up, mount will render actual component with its children, where shallowMount stubs components inside tested component, so you are not testing the actual component but some "shadow" of its true nature. To see a difference between this functions I would recommend to create a wrapper with both functions and then see what html() will return from them.
I am using React Native with functional components. componentDidMount() etc. are not available in functional components, instead I use Hooks. But Hooks don't act like lifecycle methods. I am wondering what the best practices are.
Assumed that we have a function like this one:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
// some code inside this function which is called on every component update
}, [])
server.asyncCall().then(data => {
setSomeHook(data)
})
return (<View>
{someHook ? (<Text> `someHook` was assigned </Text>) : (<Text> `someHook` was not assigned, display some ActivityIndicator instead</Text>)}
</View>)
}
Where to place server.asyncCall()? Inside or outside of useEffect?
I think you have a misunderstanding here. The convention is that all the fetching data is going to be placed inside the componentDidMount lifecycle method. React useEffect hook can replace this easily by placing an empty array of dependencies, which means you can place that call inside the useEffect you already have.
Unlike you mention in your code comment, this hook won't be triggered on each component update. It will be only be triggered once the component is being mounted. So, you should be able to do it as follows:
const ABCScreen = () => {
const [someHook, setSomeHook] = useState<any>()
useEffect(() => {
server.asyncCall().then(setSomeHook)
}, [])//only triggered when component is mounted.
In the future, you might want to check the rules of the hooks.
In this example, I have this react class:
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
The question is if I can add React hooks to this. I understand that React-Hooks is alternative to React Class style. But if I wish to slowly migrate into React hooks, can I add useful hooks into Classes?
High order components are how we have been doing this type of thing until hooks came along. You can write a simple high order component wrapper for your hook.
function withMyHook(Component) {
return function WrappedComponent(props) {
const myHookValue = useMyHook();
return <Component {...props} myHookValue={myHookValue} />;
}
}
While this isn't truly using a hook directly from a class component, this will at least allow you to use the logic of your hook from a class component, without refactoring.
class MyComponent extends React.Component {
render(){
const myHookValue = this.props.myHookValue;
return <div>{myHookValue}</div>;
}
}
export default withMyHook(MyComponent);
Class components don't support hooks -
According to the Hooks-FAQ:
You can’t use Hooks inside of a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
As other answers already explain, hooks API was designed to provide function components with functionality that currently is available only in class components. Hooks aren't supposed to used in class components.
Class components can be written to make easier a migration to function components.
With a single state:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { state } = this;
const setState = state => this.setState(state);
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [state, setState] = useState({sampleState: 'hello world'});
return <div onClick={() => setState({sampleState: 1})}>{state.sampleState}</div>;
}
Notice that useState state setter doesn't merge state properties automatically, this should be covered with setState(prevState => ({ ...prevState, foo: 1 }));
With multiple states:
class MyDiv extends Component {
state = {sampleState: 'hello world'};
render(){
const { sampleState } = this.state;
const setSampleState = sampleState => this.setState({ sampleState });
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
}
is converted to
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div onClick={() => setSampleState(1)}>{sampleState}</div>;
}
Complementing Joel Cox's good answer
Render Props also enable the usage of Hooks inside class components, if more flexibility is needed:
class MyDiv extends React.Component {
render() {
return (
<HookWrapper
// pass state/props from inside of MyDiv to Hook
someProp={42}
// process Hook return value
render={hookValue => <div>Hello World! {hookValue}</div>}
/>
);
}
}
function HookWrapper({ someProp, render }) {
const hookValue = useCustomHook(someProp);
return render(hookValue);
}
For side effect Hooks without return value:
function HookWrapper({ someProp }) {
useCustomHook(someProp);
return null;
}
// ... usage
<HookWrapper someProp={42} />
Source: React Training
you can achieve this by generic High order components
HOC
import React from 'react';
const withHook = (Component, useHook, hookName = 'hookvalue') => {
return function WrappedComponent(props) {
const hookValue = useHook();
return <Component {...props} {...{[hookName]: hookValue}} />;
};
};
export default withHook;
Usage
class MyComponent extends React.Component {
render(){
const myUseHookValue = this.props.myUseHookValue;
return <div>{myUseHookValue}</div>;
}
}
export default withHook(MyComponent, useHook, 'myUseHookValue');
Hooks are not meant to be used for classes but rather functions. If you wish to use hooks, you can start by writing new code as functional components with hooks
According to React FAQs
You can’t use Hooks inside of a class component, but you can
definitely mix classes and function components with Hooks in a single
tree. Whether a component is a class or a function that uses Hooks is
an implementation detail of that component. In the longer term, we
expect Hooks to be the primary way people write React components.
const MyDiv = () => {
const [sampleState, setState] = useState('hello world');
render(){
return <div>{sampleState}</div>
}
}
You can use the react-universal-hooks library. It lets you use the "useXXX" functions within the render function of class-components.
It's worked great for me so far. The only issue is that since it doesn't use the official hooks, the values don't show react-devtools.
To get around this, I created an equivalent by wrapping the hooks, and having them store their data (using object-mutation to prevent re-renders) on component.state.hookValues. (you can access the component by auto-wrapping the component render functions, to run set currentCompBeingRendered = this)
For more info on this issue (and details on the workaround), see here: https://github.com/salvoravida/react-universal-hooks/issues/7
Stateful components or containers or class-based components ever support the functions of React Hooks, so we don't need to React Hooks in Stateful components just in stateless components.
Some additional informations
What are React Hooks?
So what are hooks? Well hooks are a new way or offer us a new way of writing our components.
Thus far, of course we have functional and class-based components, right? Functional components receive props and you return some JSX code that should be rendered to the screen.
They are great for presentation, so for rendering the UI part, not so much about the business logic and they are typically focused on one or a few purposes per component.
Class-based components on the other hand also will receive props but they also have this internal state. Therefore class-based components are the components which actually hold the majority of our business logic, so with business logic, I mean things like we make an HTTP request and we need to handle the response and to change the internal state of the app or maybe even without HTTP. A user fills out the form and we want to show this somewhere on the screen, we need state for this, we need class-based components for this and therefore we also typically use class based components to orchestrate our other components and pass our state down as props to functional components for example.
Now one problem we have with this separation, with all the benefits it adds but one problem we have is that converting from one component form to the other is annoying. It's not really difficult but it is annoying.
If you ever found yourself in a situation where you needed to convert a functional component into a class-based one, it's a lot of typing and a lot of typing of always the same things, so it's annoying.
A bigger problem in quotation marks is that lifecycle hooks can be hard to use right.
Obviously, it's not hard to add componentDidMount and execute some code in there but knowing which lifecycle hook to use, when and how to use it correctly, that can be challenging especially in more complex applications and anyways, wouldn't it be nice if we had one way of creating components and that super component could then handle both state and side effects like HTTP requests and also render the user interface?
Well, this is exactly what hooks are all about. Hooks give us a new way of creating functional components and that is important.
React Hooks let you use react features and lifecycle without writing a class.
It's like the equivalent version of the class component with much smaller and readable form factor. You should migrate to React hooks because it's fun to write it.
But you can't write react hooks inside a class component, as it's introduced for functional component.
This can be easily converted to :
class MyDiv extends React.component
constructor(){
this.state={sampleState:'hello world'}
}
render(){
return <div>{this.state.sampleState}
}
}
const MyDiv = () => {
const [sampleState, setSampleState] = useState('hello world');
return <div>{sampleState}</div>
}
It won't be possible with your existing class components. You'll have to convert your class component into a functional component and then do something on the lines of -
function MyDiv() {
const [sampleState, setSampleState] = useState('hello world');
return (
<div>{sampleState}</div>
)
}
For me React.createRef() was helpful.
ex.:
constructor(props) {
super(props);
this.myRef = React.createRef();
}
...
<FunctionComponent ref={this.myRef} />
Origin post here.
I've made a library for this. React Hookable Component.
Usage is very simple. Replace extends Component or extends PureComponent with extends HookableComponent or extends HookablePureComponent. You can then use hooks in the render() method.
import { HookableComponent } from 'react-hookable-component';
// 👇👇👇👇👇👇👇👇
class ComponentThatUsesHook extends HookableComponent<Props, State> {
render() {
// 👇👇👇👇👇👇
const value = useSomeHook();
return <span>The value is {value}</span>;
}
}
if you didn't need to change your class component then create another functional component and do hook stuff and import it to class component
Doesn't work anymore in modern React Versions. Took me forever, but finally resulted going back to go ol' callbacks. Only thing that worked for me, all other's threw the know React Hook Call (outside functional component) error.
Non-React or React Context:
class WhateverClass {
private xyzHook: (XyzHookContextI) | undefined
public setHookAccessor (xyzHook: XyzHookContextI): void {
this.xyzHook = xyzHook
}
executeHook (): void {
const hookResult = this.xyzHook?.specificHookFunction()
...
}
}
export const Whatever = new WhateverClass() // singleton
Your hook (or your wrapper for an external Hook)
export interface XyzHookContextI {
specificHookFunction: () => Promise<string>
}
const XyzHookContext = createContext<XyzHookContextI>(undefined as any)
export function useXyzHook (): XyzHookContextI {
return useContext(XyzHookContextI)
}
export function XyzHook (props: PropsWithChildren<{}>): JSX.Element | null {
async function specificHookFunction (): Promise<void> {
...
}
const context: XyzHookContextI = {
specificHookFunction
}
// and here comes the magic in wiring that hook up with the non function component context via callback
Whatever.setHookAccessor(context)
return (
< XyzHookContext.Provider value={context}>
{props.children}
</XyzHookContext.Provider>
)
}
Voila, now you can use ANY react code (via hook) from any other context (class components, vanilla-js, …)!
(…hope I didn't make to many name change mistakes :P)
Yes, but not directly.
Try react-iifc, more details in its readme.
https://github.com/EnixCoda/react-iifc
Try with-component-hooks:
https://github.com/bplok20010/with-component-hooks
import withComponentHooks from 'with-component-hooks';
class MyComponent extends React.Component {
render(){
const props = this.props;
const [counter, set] = React.useState(0);
//TODO...
}
}
export default withComponentHooks(MyComponent)
2.Try react-iifc: https://github.com/EnixCoda/react-iifc
I'm currently trying to create a component which manages several linked dropdowns and elements on a page. In addition, this element supplies a rather fancy navigation element, containing anchor links which automatically scroll to the desired element in this component.
The problem is that the actual contents of the component are completely dynamic and partially determined by the content manager in the CMS. There are several sub components that are always present, but apart from that the content manager can add any number of sections, (using various named and an unnamed ) and each of these sections should be added to the navigation of the component.
I see 3 options:
For every component added, it's title and unique id is added to a property array on the parent component. This, however, is a bit prone to errors. (Unfortunately I will have no control over the eventual backend implementation, so I'm trying to create a foolproof system to avoid to much wasted time.)
Due to other components, I'm already using Vuex to manage most of the data in the app. I figured I use a simple directive, to be added on each item in the parent component. This directive is responsible for adding that element to the Vuex store. The parent component simply reads the contents of the store and generates the navigation based on this.
The problem here is that, as far as I can tell, I have to use the vNode argument in the bind hook of my directive to access the Vuex store. This seems... hacky. Is there anything wrong with this approach?
In the mounted hook of my parent component, I traverse the DOM, searching for elements with a particular data-property, and add a link to each of these elements to my navigation. Seems prone to failure and fragile.
What is the preferred approach for this situation?
As a follow up question - What is the correct use case for the vNode argument in a vue directive? The documentation seems rather sparse on this subject.
I would steer away from using a directive in this case. In Vue 2, the primary use case for directives is for low level DOM manipulation.
Note that in Vue 2.0, the primary form of code reuse and abstraction
is components - however there may be cases where you just need some
low-level DOM access on plain elements, and this is where custom
directives would still be useful.
Instead I would suggest a mixin approach, where your mixin essentially registers your components that should be included in navigation with Vuex.
Consider the following code.
const NavMixin = {
computed:{
navElement(){
return this.$el
},
title(){
return this.$vnode.tag.split("-").pop()
}
},
mounted(){
this.$store.commit('addLink', {
element: this.navElement,
title: this.title
})
}
}
This mixin defines a couple of computed values that determine the element that should be used for navigation and the title of the component. Obviously the title is a placeholder and you should modify it to suit your needs. The mounted hook registers the component with Vue. Should a component need a custom title or navElement, mixed in computed properties are overridden by the component's definition.
Next I define my components and use the mixin.
Vue.component("child1",{
mixins:[NavMixin],
template:`<h1>I am child1</h1>`
})
Vue.component("child2",{
mixins:[NavMixin],
template:`<h1>I am child2</h1>`
})
Vue.component("child3",{
template:`<h1>I am child3</h1>`
})
Note that here I am not adding the mixin to the third component, because I could conceive of a situation where you may not want all components included in navigation.
Here is a quick example of usage.
console.clear()
const store = new Vuex.Store({
state: {
links: []
},
mutations: {
addLink (state, link) {
state.links.push(link)
}
}
})
const NavMixin = {
computed:{
navElement(){
return this.$el
},
title(){
return this.$vnode.tag.split("-").pop()
}
},
mounted(){
this.$store.commit('addLink', {
element: this.navElement,
title: this.title
})
}
}
Vue.component("child1",{
mixins:[NavMixin],
template:`<h1>I am child1</h1>`,
})
Vue.component("child2",{
mixins:[NavMixin],
template:`<h1>I am child2</h1>`
})
Vue.component("child3",{
template:`<h1>I am child3</h1>`
})
Vue.component("container",{
template:`
<div>
<button v-for="link in $store.state.links" #click="scroll(link)">{{link.title}}</button>
<slot></slot>
</div>
`,
methods:{
scroll(link){
document.querySelector("body").scrollTop = link.element.offsetTop
}
},
})
new Vue({
el:"#app",
store
})
h1{
height:300px
}
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.js"></script>
<div id="app">
<container>
<child1></child1>
<child3></child3>
<child2></child2>
</container>
</div>
This solution is pretty robust. You do not need to parse anything. You have control over which components are added to the navigation. You handle potentially nested components. You said you don't know which types of components will be added, but you should have control over the definition of the components that will be used, which means its relatively simple to include the mixin.
I'm wanting to assert that a component gets called from within another component with the correct arguments.
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
I'm pretty sure I want to use a sinon spy and do something like this
const titleSpy = sinon.spy(Title, render)
expect(titleSpy).to.be.calledWith( '< some title >' )
but with regards to React and Enzyme, I'm not really sure what I should be spying on. (Because apparently it's not render!)
In my spec file I am importing Title and console.loging it's value to find a function to spy on and I get:
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, Object.getPrototypeOf(_class).apply(this, arguments));
}
Any ideas on how I can do this? Is it a case of going through and finding the element and checking it's attributes? If so that seems a bit...messy and seems like it goes against the principle of the Shallow render ("Shallow rendering is useful to constrain yourself to testing a component as a unit").
If you're just checking the value of properties passed to the component, you don't need sinon. For example, given the following component:
export default class MyComponent extends React.Component {
render() {
return (
<MyComponent myProp={this.props.myProp} />)
}
}
Your test might look like this:
describe('MyComponent ->', () => {
const props = {
myProp: 'myProp'
}
it('should set myProp from props', () => {
const component = shallow(<MyComponent {...props} />)
expect(component.props().myProp).to.equal(props.myProp)
})
})
You can achieve it with the help of .contains() method, without messing up with spies.
If you have a component:
<Foo>
<Title title="A title" url="http://google.com" />
</Foo>
You can make such an assertion:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A title" url="http://google.com" />)).to.equal(true);
Such will fail:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A wrong title" url="http://youtube.com" />)).to.equal(true);
This is an older question, but my approach is a little different than the existing answers:
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
ie. You're wanting to check that the component being tested renders another component, and passes the correct prop(s) to it.
So if the component being tested looks something like:
const MyComp = ({ title, url }) => (
<div>
<Title title={title} url={url} />
</div>
)
Then the test could look something like:
import Title from 'path/to/Title';, u
it('renders Title correctly', () => {
const testTitle = 'Test title';
const testUrl = 'http://example.com';
const sut = enzyme.shallow(<MyComp title={testTitle} url={testUrl} />);
// Check tested component rendered
expect(sut.exists).toBeTruthy();
// Find the Title component in the subtree
const titleComp = sut.find(Title); // or use a css-style selector string instead of the Title import
// Check that we found exactly one Title component
expect(titleComp).toHaveLength(1);
// Check that the props that were passed were our test values
expect(titleComp.prop('title')).toBe(testTitle);
expect(titleComp.prop('url')).toBe(testUrl);
});
I generally find Enzyme's functions to be very useful for all kinds of checks about components, without needing other libraries. Creating Sinon mocks can be useful to pass as props to components, to (for example) test that a callback prop is called when a button is clicked.