I want to make a test mocking a function in my react component, for example I have:
const app=()=>{
const [state,setState]=useState("");
const func=(item)=>{
setState(item)
}
return(
<div>{state}<div/>
)
}
and I want to test that function called "func" with jest and enzyme
for this example I have :
let wrapper=mount(<App/>)
test("testing",()=>{
let item="test"
//mock function -> func(item)
// how to mock that function
}
)
can someone help me with the syntax?
While you can spy on and mock results from method properties in class based components, you can't use the same approach for functional components.
What you should do is to trigger or simulate the particular behaviour that calls the func method in your functional components, and then test the expected behaviour (i.e if the props method is called, or changes in DOM elements, etc).
If it is triggered by the useEffect() hook, you will have to trigger that hook by updating the props (using setProps) that is used in the dependency array.
Related
I'm trying to write a unit test for a Card compenent using Jest. This component takes a framer-motion value and a ref as Props. This values can only be created using the useMotionValue and useRef hook.
interface Props {
currentIndex: MotionValue<number>;
containerRef: RefObject<HTMLDivElement>;
}
Currently, I'm able to pass in a dummy value for the ref using document.createElement, but when i tried creating a motion value to pass into the component, I get an error saying that react hooks cannot be used ouside a component
it("renders a card", () => {
const container = document.createElement("div");
render(
<Card
containerRef={{ current: container }}
currentIndex={******} // motion value goes here
/>
);
});
I had to render the component in a parent component first and then test the parent Component but i was wondering, is there a way to mock the motion value and pass it in as a prop?
The easiest way would be to mock useMotionValue implementation. You can use jest.mock at the top of your file where you write your test for the component that uses this hook. Remember also to not mock other functionality by requiring actual implementation.
// at the top of the file that tests component with useMotionValue call
jest.mock('framer-motion', () => ({
...jest.requireActual('framer-motion'),
useMotionValue: jest.fn().mockReturnValue({
// put here the mock implementation of MotionValue methods
})
}));
Say I have a simple React functional component that largely just observes changes in context and renders some view of the state from the context.
export default function Observer(props) {
// I have a separate dispatch context for performance. This component doesn't dispatch
const state = useContext(SomeContext);
return (
<div>Foo is now {state.foo}</div>
)
}
For testing under jest/react testing library, I was imagining a few possibilities:
Wire up a wrapper for the context that just instantiates different
states for each test. This seems like it would be easy and straightforward. Is this
the typical pattern?
Instantiate each test with real initial state.
Then somehow change state and expect the component to update.
Perhaps using dispatch from the test code somehow. Most examples I see fire events, but this
component doesn't dispatch anything, it just observes.
Render a larger part of the app, with other components that update the state.
This seems like a bad choice for a unit test of a small, simple
component.
The first option is the way to go. As you already mentioned, the component in question is a consumer of the context. So, if it is provided with some value for context, it will most certainly use it.
You can create a simple function to pass different context values, which is provided via provider and is rendered. Then you assert what happens,
const renderComponent = (contextValue) => {
render(
<SomeContextProvider value={contextValue}>
<Observer />
</SomeContextProvider>
);
};
test('my test case name', () => {
render({foo: abc});
expect(screen.getByText('Foo is now abc')).toBeInTheDocument();
})
Some good reading here https://testing-library.com/docs/example-react-context/
How do I test a prop(callback) function that is called conditionally
I have a component which passes a prop function to its child on a condition like:
if(this.props.myFun) {
this.props.myFun();
}
to test this, I have two cases here:
1. Where the prop is passed to the component
<ChildComp myFun={value => value } /> and I can test it from the child-
const comp = mountWithIntl(<ChildComp myFun={value => value } />);
expect(comp.instance().props.myFun).toHaveBeenCalled();
Where the prop is not passed: I trying like
const comp = mountWithIntl(<MyComp />);
expect(comp.instance().props.myFun).not.toHaveBeenCalled();
But, Since If I mock the prop while mounting the component, the
method will be called. But how do I test the undefined or unmocked
prop?
If I dont pass the prop and do:
expect(comp.instance().props.myFun).not.toHaveBeenCalled();
I will get:
jest.fn() value must be a mock function or spy
which I cant do as per my code
please help
I don't think you can test that a not passed function does not get called. What you can do to test if it is get rendered without that function. That should be enough
If you have a React component that calls a custom hook that fetches data, what is the best way to mock that internal custom hook result when testing the React component? I see 2 main approaches:
1) Jest.mock the custom hook. This seems to be the most recommended approach, but it seems like it requires the test to have more knowledge of internal implementation details and what it might need to mock than what the props interface of the component might suggest (assuming use of prop-types or TypeScript)
2) Use a dependency injection approach. Declare the hook as a prop, but default it to the real hook so you don't have to set it everywhere you render the component, but allow overriding with a mock for tests. Here is a contrived codesandbox example with a test that mocks a custom hook:
https://codesandbox.io/s/dependency-inject-custom-hook-for-testing-mjqlf?fontsize=14&module=%2Fsrc%2FApp.js
2 requires more typing, but seems easier to work with for testing. However, tests already have to have knowledge of internal implementation details of component to test any conditional logic for rendered output, so maybe that's not important and 1 is the best approach. Is 1 the way to go? What tradeoffs do you see? Am I missing another approach altogether?
To mock your custom hook using jest.
import * as useCustomHook from '../hooks/useCustomHooks'
const spy = jest.spyOn(useCustomHook, 'default')
spy.mockReturnValue({
name: 'test'
})
This question is a few months old, but if you haven't found a good solution, I wrote a package that might help. I went through a similar thought process, including "what if I inject the hooks into the component?" Things got weird.
I basically wanted a connecter to avoid an extra wrapper for presentational components just to test them.
I came up with react-hooks-compose, which lets you keep your hooks and your presenters separate, and test them individually or together: https://www.npmjs.com/package/react-hooks-compose
export const useFetch = () => {
const [user, setUser] = useState();
useEffect(() => {
fetchData('some-url') // <-- Fetches data on mount
.then(res => setUser(res.data));
}, []);
return {user};
}
// composeHooks passes the values from your hooks as props
export const UserPresenter = ({user}) => {
return <div>You fetched data for: {user.name}</div>;
}
export default composeHooks({ useFetch })(DataPresenter);
Now you don't have to mock the hook, you can just test the presenter with a prop:
it('presents user', () => {
const { queryByText } = render(<UserPresenter user={{name: 'Mary'}} />); // <-- Named export
expect(queryByText('Mary')).toBeTruthy();
});
Or, you have the option of a higher-level integration test:
it('fetches data', () => {
fetchData.mockResolvedValue('Mary');
const { queryByText } = render(<UserWithData />); // <-- Default export
expect(queryByText('Mary')).toBeFalsy();
return wait(() => {
expect(queryByText('Mary')).toBeTruthy();
});
});
You can even unit test the hook if you like.
Why don't you mock the underlying method that makes the api call instead?
For example, if you're retrieving data with fetch(), you mock that instead. That way you can define custom response for that call, which will make testing the hook itself easy.
With mocking hook itself you never know if real one works well all together with your component.
With passing hook as a prop it would be really hard to make hooks communicate with each other. E.g. when you need your custom hook to call setter from the same component useState. You would need to extend custom hook with more and more parameters.
You may mock external API call - I mean mocking fetch or XHR. It still needs to know some implementation details - the fact you're running HTTP request - but there are less things your test should know about.
I have a reactjs component that has a method doSomething:
doSomething()
{
this.setState({
checked: !this.state.checked
}, function () {
if (this.state.checked) {
store.dispatch({type: 'SOMEACTION', data: true});
}
});
}
What would the chai/mocha unittest look like for this method/component? How can I test a dispatch has called?
Replace the dispatch with a spy and check that is was called with the correct arguments.
You can test that your action correctly dispatched to reducers correctly in another unit test somewhere using redux-mock-store.
You can also use redux-mock-store to mock the store here.
Although why are you calling dispatch on the store directly? You usually want to just call it on the props like so: this.props.dispatch() as dispatch gets passed automatically when you use mapStateToProps. This would mean you could just pass a spy down as a prop instead and not have to mock out the entire store.
You could use expect, for instance use spy to ensure the method is called as well as assert that state has changed.
// spy on the method to assert that it has been called
const doSomething = expect.spyOn(ExampleComponent, 'doSomething');
// simulate some behaviour
...
// assert method called
expect(doSomething).totHaveBeenCalled();
// assert state changed
assert.equal(store.getState()..., ...)
Also, you can split this into multiple tests.