Hey I am using following this tutorial for using TDD with react the Display Component is a class but I wanted to make it functional component as I need to learn more about them in order to work with hooks later on, but in the following test failed with Error: ShallowWrapper::state() I kept trying and figured out a way to get the value but doing the following wrapper.props().children.props.displayValue but when I added another component the test broke as wrapper.props().children return a list now and I need to use it with an index for each nested component, is there's a better way to get those value as I think this is a really bad way to get the Calculator internal properties
import React, {useState} from 'react';
import './Calculator.css';
import Display from "../Display/Display";
import Keypad from "../Keypad/Keypad";
const Calculator = () => {
// value to be displayed in <Display />
const [displayValue, setDisplayValue] = useState('0');
// values to be displayed in number <Keys />
const [numbers, setNumbers] = useState([]);
// values to be displayed in operator <Keys />
const [operators, setOperators] = useState([]);
// operator selected for math operation
const [selectedOperator, setSelectedOperator] = useState('');
// stored value to use for math operation
const [storedValue, setStoredValue] = useState('');
const callOperator = () => {
console.log('call operation');
};
const setOperator = () => {
console.log('set operation');
};
const updateDisplay = () => {
console.log('update display');
};
return(
<div className="calculator-container" >
<Display displayValue={displayValue} />
<Keypad
callOperator={callOperator}
numbers={numbers}
operators={operators}
setOperator={setOperator}
updateDisplay={updateDisplay}
/>
</div>
);
};
export default Calculator;
Spec file
it('should render the <Display /> Component', () => {
expect(wrapper.containsMatchingElement([
<Display displayValue={wrapper.props().children.props.displayValue}/>,
<Keypad
callOperator={wrapper.props().children.props.callOperator}
numbers={wrapper.props().children.props.numbers}
operators={wrapper.props().children.props.operators}
setOperator={wrapper.props().children.props.setOperator}
updateDisplay={wrapper.props().children.props.updateDisplay}
/>
])).toEqual(true);
})
so how to convert this test to work with functional component
it('should render the Display and Keypad Components', () => {
expect(wrapper.containsAllMatchingElements([
<Display displayValue={wrapper.instance().state.displayValue} />,
<Keypad
callOperator={wrapper.instance().callOperator}
numbers={wrapper.instance().state.numbers}
operators={wrapper.instance().state.operators}
setOperator={wrapper.instance().setOperator}
updateDisplay={wrapper.instance().updateDisplay}
/>
])).toEqual(true);
});
I have managed to green the test like the following
it('should render the <Display /> and <Keypad /> Components', () => {
const display = wrapper.find(Display);
const keypad = wrapper.find(Keypad);
expect(wrapper.containsMatchingElement(
<Display displayValue={display.props().displayValue}/>,
<Keypad
callOperator={keypad.props().callOperator}
numbers={keypad.props().numbers}
operators={keypad.props().operators}
setOperator={keypad.props().setOperator}
updateDisplay={keypad.props().updateDisplay}
/>
)).toEqual(true);
})
but still wanna know if there's anyway better as right now it seems to be Blackbox testing.
There is no equivalent. Functional components don鈥檛 have state, or an instance.
useState isn鈥檛 the same as component state - React manages that; it鈥檚 not directly tied to the instance - and since React provides no hooks for hooks, there鈥檚 no good way for a testing tool to have any interaction with hooks.
@ljharb well what about the other method such 'updateDisplay' or 'setOperator' is there is a way to reach them so i can test them as method got called or not !? Or even a work around it as i really kept searching all day long with no clue, thanks in advance.
@MuhmdRaouf since those are not methods (a method in JS is a function attached to an object), but rather, standalone functions - and functions defined inside a closure - it is impossible to directly test them unless they're exposed. The only thing you can do is to test their effects by making sure that whatever their job is, gets done.
In your case, you are directly exposing them, as props on a Keypad element - so you can extract them that way (like you're already doing here).
The thing is they are not exposed, yet as the component doesn't have instance i cant spyOn it to green the following test, one way to test it as you mentioned is by assert on the effect that method doing, or export the method just for the sake of the test, so i will lose the privacy in order to test and i hate doing so to be honest, so is there's any tip to.green the following test.
describe('mounted <Calculator />', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(<Calculator/>);
});
it('calls updateDisplay when a number key is clicked', () => {
const spy = jest.spyOn(wrapper.find(Keypad).props(), 'updateDisplay');
wrapper.update();
expect(spy).toHaveBeenCalledTimes(0);
wrapper.find('.number-key').first().simulate('click');
expect(spy).toHaveBeenCalledTimes(1);
});
});
You can't spy on it before it's passed down, no, but you don't have to. Shallow-render the component, and directly call the function, and assert on what it does.
@ljharb Thanks a lot mate, that was really helpful 馃槃
This seems answered; happy to reopen it if not.
To be honest i think this is just a work around(even its right as i should test behavior not implementation details) yet i would love to have a more options and let me do it my way, for me as i am TDDing all the time even in my work, i might tend to go to class component just for the sake of simplicity of testing, testing already hard we would i make it even harder, class component easier to test, However Hooks are great and i find it much cleaner and organized (call me crazy) but i really do, so this question might raise some more options that make testing functional component easier.
Most helpful comment
To be honest i think this is just a work around(even its right as i should test behavior not implementation details) yet i would love to have a more options and let me do it my way, for me as i am TDDing all the time even in my work, i might tend to go to class component just for the sake of simplicity of testing, testing already hard we would i make it even harder, class component easier to test, However Hooks are great and i find it much cleaner and organized (call me crazy) but i really do, so this question might raise some more options that make testing functional component easier.