I have situation in my unit test case for a react application, where in a function calls for another function received in props from parent component. The parent component functions definition is something like this:
onSavePropClick(action) {
const save = this.saveProperty(action);
if(action === SAVE){
return () => new Promise(() => {
resolve(this.calculate().then(save));
});
}
return save;
}
This function call has been passed as props to the child component as
<MyComponent finalSave={this.onSavePropClick(SAVE)} onClose={()=>this.setState({closeWindow: true})} />
MyComponent has a function:
savingAndShowResults() {
const { finalSave, onClose } = this.props;
finalSave().then(() => {
onClose();
});
return true;
}
Now when I have a test for the executed, it throws me error as “Cannot read property then of undefined”, the test is as follows
const initialProps={
finalSave: jest.fn(),
onClose: jest.fn()
};
it(‘should handle saving and show results’, () => {
const component = shallow(
<MyComponent {...initialProps} />
);
component.instance().savingAndShowResults();
expect(initialProps.finalSave).toHaveBeenCalled();
expect(initialProps.onClose).toHaveBeenCalled();
});
I am not able to figure out why even on resolving in return in promise of Parent component’s function, gives me this error.
Please suggest.
Assuming initialProps.finalSave is a mock function, you need to make sure you're returning a promise from initialProps.finalSave:
const initialProps = {
finalSave: jest.fn().mockImplementation(() => Promise.resolve());
...
};
Related
I am using function component where is there is a arrow function.
const handleOnConfig = (cfg) => {
const layout = { ...config };
setConfig(layout);
};
Now, I want to write unit test for that function.
So, I did following in my test file. I used
beforeEach(() => {
let props = {user:user}
view = shallow(<Views {...props} />).instance();
});
test('should call config change call back', () => {
const handleOnConfig = jest.spyOn(view,'handleOnConfig').mockImplementation(() => config);
expect(handleOnConfig).toHaveBeenCalledTimes(1);
});
But, this test case gives error:
TypeError: Cannot read property 'handleOnConfig' of null
Any help would greatly appreciated
Edit:
Views component
const Views = forwardRef(({ user, id }, ref) => {
useEffect(() => {
handleOnConfig();
}, []);
return (
<div>
<Component
user={user}
id={id}
name='Component'
/>
</div>
);
})
export default Views;
Not a problem with spyOn() directly: your view variable is null.
Try using .dive() in the return from shadow():
beforeEach(() => {
let props = {user:user}
view = shallow(<Views {...props} />).dive().instance(); // <-- added .dive()
});
I have a component Header which has an action prop which has an onClick event handler which calls a function with a parameter status. Example :
// App.tsx
/// React imports
const App = () => {
let status = new B()
const someFunc = (val) => {
if (val instanceof A) {
console.log('inside A')
} else if (val instanceof B) {
console.log('Inside B')
} else {
console.log('No matchers')
}
}
return (
<>
<Header id="someID" action={{ text: 'Change', onClick: () => somefunc(status) }} />
</>);
}
I am writing a unit test to simulate onClick to have been called with status like this :
// App.test.js
describe("test", ()=>{
it('simulate click', () => {
const createComponent = () => (<App />)
const status = new A()
const wrapper = mount(createComponent())
wrapper
.find('#someID')
.simulate('click', {status: status})
expect(logSpy).toHaveBeenCalledWith('inside A') // expected 'Inside B'
})
})
Somehow, it is still catching the status defined in App.tsx and not in the test file. Where could I be wrong?
PS: I know we should not business implementation in React, but I have the necessity to test business implementation. Any help is appreciated.
The onClick handler is defined as
onClick: () => somefunc(status) not as
onClick: (status) => somefunc(status) or even
onClick: (event) => somefunc(event.target.value) or somesuch.
So yes, the lambda has captured the status of the component and enshrined it permanently in the little function.
I am new to Jestjs and enzyme framework and I am trying to write test cases for a particular react component and I am little stuck.
export class ProductDetailsForm extends Component{
handleMetaDataDefinition = e => {
const { value, name } = e.target;
if (name === "xmlVersion") {
this.checkSpecialCharacters(value);
}
this.setState(prevState => ({
...prevState,
[name]: value
}));
this.props.setProductDetailsFormValue({
...this.props.productDetailsForm,
[name]: value
});
};
checkSpecialCharacters = value => {
if (!value || value.match(/^[a-zA-Z0-9._-]+$/)) {
this.setState(() => ({ error: '' }));
} else {
this.setState(() => ({
error: `Special characters and operators such as !##$%^&*()+{}:;?|\\[]'"= are not allowed`
}));
}
}
render(){
return(
<div>
<MetaDataDefinition
readOnly={false}
metaData={this.state}
handleMetaDataDefinition={this.handleMetaDataDefinition}
validateVersion={this.validateVersion}
/>
</div>
);
}
}
I have started with the test case, but I am stuck and unable to proceed how to work on the function handleMetaDataDefinition for full coverage including the function checkSpecialCharacters. Below is the code that I started to write for ProductDetailsForm.test.js
let wrapper;
beforeEach(() => {
wrapper = shallow(
<ProductDetailForm />
);
});
test("should call handleMetaDataDefinition", ()=> {
wrapper.find('MetaDataDefinition').props('handleMetaDataDefinition');
});
I have used some part of my actual code and not the whole code, as I need help in this specific part only to write test case for handleMetaDataDefinition and checkSpecialCharacters methods.
There're two possible option how to write your tests.
You can trigger validation from your MetaDataDefinition component and pass there needed data.
test("should call handleMetaDataDefinition", ()=> {
const component = wrapper.find('MetaDataDefinition');
fillYourComponentSomehow();
triggerAnEventSomehow();
/*For example component.find('button').simulate('click');
wrapper.update();// We can wait for updating state differently(if needed i'll take a look to doc.)
expect(wrapper.state()).toBe(stateThatYouExpect);
});
Or you can test it as 'black box'
test("should call handleMetaDataDefinition", ()=> {
const component = wrapper.find('MetaDataDefinition');
component.props().handleMetaDataDefinition(objectForMethod)
wrapper.update();
expect(wrapper.state()).toBe(stateThatYouExpect);
});
If you have HOCs around your component you'll need to find this component by class name
wrapper.find('ProductDetailsForm')
UPDATE
You can test it like
let wrapper;
let setProductDetailsFormValue;
beforeEach(() => {
setProductDetailsFormValue = jest.fn();
wrapper = shallow(
<ProductDetailForm setProductDetailsFormValue={setProductDetailsFormValue} />
);
});
test("should call handleMetaDataDefinition", ()=> {
const testObject = { target: {name: 'xmlVersion', value: '!!!123asd!'}, }
const component = wrapper.find('MetaDataDefinition');
component.props().handleMetaDataDefinition(testObject)
wrapper.update();
expect(wrapper.state().error).toBe('Special characters and operators such as !##$%^&*()+{}:;?|\\[]'"= are not allowed');
expect(wrapper.state()[xmlVersion]).toBe('!!!123asd!');
expect(setProductDetailsFormValue).toBeCalledWith({
[xmlVersion]: '!!!123asd!',
...other fields})
});
Good afternoon,
I have a component file structured like that globally :
class Component ...
render(){
const {array} = this.props
{!array.includes(value) ?
(<View ...props
id="myComponent"/>
....
</View>) :
(<View ...props
id="myOtherComponent"/>
....
</View>)
}
}
And in my test file, i'm doing the stuff like that :
describe('Testing Component', () => {
test('conditional rendering', () => {
const wrapper = shallow(<Component array={[value]}/>);
expect(wrapper.find(n => n.prop('id') === "myOtherComponent").exists(true))
});
});
But even if I modify the props sent for the array, it always returned me true... What's the keyword to check that the nested component is actually verified and rendered...
I think the error is in your expect argument.
I would use the findWhere function instead of find;
The exists method should not receive a parameter in this
case, as it only receives Enzyme's Selectors and not booleans (you can read more about it here);
Add a toBeTruthy call to the expect line.
Here's a similar situation to yours that we have a test for and it works just fine:
it('tests name', () => {
const mockComponent = shallow(<Component {...props} />);
const textNode = mockComponent.findWhere(n => n.text() === props.name);
expect(textNode.exists()).toBeTruthy();
});
So your test would end up looking like this:
describe('Testing Component', () => {
test('conditional rendering', () => {
const wrapper = shallow(<Component array={[value]}/>);
const node = wrapper.findWhere(n => n.prop('id') === 'myOtherComponent');
expect(node.exists()).toBeTruthy();
});
});
Say I have the following component:
export class ExampleComponent extends Component {
exampleMethod1 = () => {
console.log('in example 1')
}
exampleMethod2 = () => {
console.log('in example 2')
this.exampleMethod1()
}
render() {
return (
<TouchableOpacity id='touchable' onPress={exampleMethod2}><Text>Touchable</Text></TouchableOpacity>
)
}
}
This works exactly how you would expect. The button appears, and can be pressed. Both methods fire, and console log their text.
I now try to test this with jest:
describe('example tests', () => {
let wrapper
beforeEach(() => {
wrapper = shallow(<ExampleComponent/>)
})
it('this test fails. Interestingly both messages still print', () => {
const instance = wrapper.instance()
instance.exampleMethod2 = jest.fn()
wrapper.find('#touchable').simulate('press')
//wrapper.update() uncommenting this line has no effect.
expect(instance.exampleMethod2.mock.calls.length).toBe(1)
})
it('this test passes. Only the first message prints', () => {
const instance = wrapper.instnace()
instance.exampleMethod1 = jest.fn()
wrapper.find('#touchable').simulate('press')
expect(instance.exampleMethod1.mock.calls.length).toBe(1)
})
})
As annotated, the first test fails, and the original message prints, as if I had never mocked out the method. This happens irrespectively of whether wrapper.update() is run or not.
Interestingly, if we replace the onPress with a seemingly identical arrow function like so:
onPress={() => {exampleMethod2()}}
The test suddenly passes. This whole thing suggest some weird this binding shenanigans (I think?). Any explanation as to what is going on would be much appreciated!
If you want to test custom methods on component's prototype object, you should use mount function from enzyme and use spyOn to mock and trace the call to that method.
export class ExampleComponent extends Component {
exampleMethod1 = () => {
console.log('in example 1');
this.setState({ example1: true });
}
exampleMethod2 = () => {
console.log('in example 2')
this.exampleMethod1()
}
render() {
return (
<TouchableOpacity id='touchable' onPress={exampleMethod2}><Text>Touchable</Text></TouchableOpacity>
)
}
}
describe('example tests', () => {
let wrapper
beforeEach(() => {
wrapper = mount(<ExampleComponent/>)
})
afterAll(() => { wrapper = null; })
it('some desc here', () => {
const instance = wrapper.instance();
spyOn(instance, 'exampleMethod1').and.callThrough();
expect(instance.setState).toHaveBeenCalled();
});
})