I'm trying to use Jest to integration test some of our more complex react components which are made up of a number of sub components, the theory being to test high up the stack of components to be closer to how the user experiences the functionality.
I'm using redux for state management and calls are made to sagas as the component is mounted and state updated once the ajax calls are returned via fetch calls in the sagas.
The problem I'm having is when I build my component in Jest I'm only getting the inital state of the component and its like none of the saga's are being called - or at least the results of their fetch calls are not being correctly mocked.
I can see the saga's are being called from debugging statements.
My test is:
import React from 'react';
import {fireEvent, cleanup, waitForDomChange, wait} from '#testing-library/react'
import {renderWithRedux} from '../../../../helpers/testSetup'
import "#testing-library/jest-dom/extend-expect"
import {ComponentContainer} from '../componentContainer'
import {
ajaxCall1,
ajaxCall2,
ajaxCall3
} from '../../../../__mocks__/componentMockData'
// test setup
afterEach(cleanup)
beforeEach(() => {
fetch.resetMocks();
});
// consts
const match = {
params: {
id: "23423"
}
}
// tests
describe('Component Container', () => {
it('Rendering component', async () => {
// 1. Arrange
fetch
.once(ajaxCall1())
.once(ajaxCall2())
.once(ajaxCall3())
// building my component here
const {getAllByText,getByTestId,findByTestId,store} = renderWithRedux(<ComponentContainer match={match} />)
expect(getAllByText("Loading component")[0]).toBeVisible()
await findByTestId('componentHeaderCard') // this times out
// 2. Act
// 3. Assert
})
})
From my research I don't seem to be able to find something which tests building a component which inside makes a number of calls - only tests which mock one fetch.
Is what I'm attempting possible?
This might not be the "best" solution but it has worked.
Instead of having different fetch calls within my sagas I moved them out into their own functions which I then call within my saga. This has allowed me to mock those individual functions.
Related
If I have a stateful functional component that uses state hooks, how can I manipulate the state from outside the component? I want to be able change the state in my tests so that I can test how the dom changes.
I want to be able to change the value of something like this from outside of the component:
const [sent, setSent] = useState(false);
I would not test the internal state of the component but rather what this state might represent. The usage of the state is an implementation detail and you should test if the specification is implemented correctly and not how it is implemented.
In your case I would use the following approach:
Call the functionality that should set the sent state (button click,
form submit)
Test if the sent status is handled correctly in your
component (a success message is shown?)
Testing loading states of api calls can be achieved with mocks. If you don't use some fancy library but just do normal await apiCall() then you can use following approach:
Mock your api call (you probably already doing it)
Return a Promise() from the mock that will not be resolved
Example with enzyme:
import { apiCall } from '../api';
jest.mock('../api');
// ...
it('Contains a <Loading /> on loading', () => {
// The promise won't be resolved so the loading state will persist
apiCall.mockReturnValue(new Promise(() => null));
// await act... and wrapper.update() might not be needed
// depending on your implementation
await act(async () => {
wrapper.find(SubmitButton).simulate('click');
});
wrapper.update();
expect(wrapper.find(Loading)).toHaveLength(1);
});
I want to structure my react project and set my APIs and methods that makes the calls in a separate file. I am coming from Angular background, in Angular there are services which handle all http calls.
So, assume that i have a functional component called Supplier that have two files, Supplier.js and Supplier.css. I want to add a new file and call it SupplierServices.js in this file i want to write all methods that makes the calls to the server and simply import it in Supplier.js.
how to achieve this?
Your file with request.js:
import axios from 'axios';
export default () => axios.get('/test');
Your component component.js:
import React from 'react';
import {request} from './request';
const Component = () => {
.....
request();
}
Components are just functions. You don't necesarily need to render anything in them, they can hold logic or API calls if you want. Before hooks, and when working without a state manager like Redux, you usually saw either HOCs (higher order components) or render props be used to compose logic together, here are some examples:
Using HOCs
Using hooks and render props
You can use hooks alone as well
There's also nothing stopping you from 'emulating' services like in your example, it's mostly a matter of taste and being aware of the pros and cons of each method.
You can do it like this
1. Create the method you want to export inside suppliers devices.js.
SupplierServices.js
Export const fetchData = async () => {
Const res = await axios.get(your url)
Console.log(res)
}
2. Now import the fetchData method from supplierServices
Import { fetchData } from './SupplierServices'
Const Supplier = () => {
useEffect(.() =>{
fetchData ()
} )
}
i used to use flux architecture - this pattern can be use in every framework.
More info about implementing redux
└─ client
└─ redux
└─ services
└─ actions
└─ reducers
Later encapsulating logic parts into individual parts (user.service, event.service, transaction service) etc.
Usually, event calls actions, which call service (in service promisses are operated asynchronously - try-catch), which return data(success) or error - that call the reducer and change the application's UI state (a bit complexity - but the advantage is the possibility of management all components - regardless of their relationality (global status).
I'm writing some unit tests for a componente that renders a FlatList and I need to simulate some actions like scrollTo and scrollToEnd.
Anyone knows how can I reach this?
I'm using Jest and Enzyme for tests.
import React form "react";
import { FlatList } from 'react-native';
import { mount } from 'enzyme';
describe("<FlatList/>", () => {
const callback = jest.fn();
it("how to simulate scroll?", () => {
const list = mount(<FlatList onEndReach={callback}/>);
//how to simulate scrool and reach the end of the list?
expect(callback).toHaveBeenCalled();
})
})
Calling scrollToEnd() from instance of mounted object didn't work.
const flatList = wrapper.find(FlatList);
flatList.first().instance().scrollToEnd();
Expected mock function to have been called, but it was not called.
In my simulation I want to call the callback function when Flatlist.scrollToEnd() is called.
Since you test your component not FlatList itself I believe you better think in somehow abstract way: instead of working on low-level thing like emulating scroll just call the props:
list.find(FlatList).prop('onEndReach')();
or with simulate() syntax sugar:
list.find(FlatList).simulate('endReach');
To me it's not only easier than simulating scrolling but also better maintainable.
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've been working with ApolloJS in React (via react-apollo) for the past several months and have learned a number of the tricks and challenges associated with unit testing Apollo-wrapped components.
When testing a component that is directly wrapped with Apollo, I export and test the component before it is wrapped with HoC returned by graphql. When testing a component that has an Apollo-wrapped component as a descendant, I use Enzyme's shallow rendering whenever possible to prevent that descendant from mounting. If full-DOM rendering via mount is required, I use a MockedProvider from Apollo's test utils so that the descendants don't throw an error trying to access this.context.
I have not found a solution, however, for the following case: a component with Apollo-wrapped descendants needs to be tested using full-DOM rendering but I also need to make assertions involving the component instance (e.g. state, instance methods, etc). To avoid issues with descendants, I have to wrap the component in a mocked provider, but that means than any assertions on the wrapper are operating on the MockedProvider instance and not the component I want to be testing.
An example:
import { mount } from 'enzyme'
import { MockedProvider } from 'react-apollo/lib/test-utils'
// This component has descendants that are wrapped in Apollo and
// thus need access to `this.context` provided by an Apollo provider
import Assignments from 'app/components/assignments
...
describe('<Assignments />', function() {
it('sets sorted assignments in initial state', function() {
const assignments = [...]
const wrapper = mount(
<MockedProvider>
<Assignments assignments={assignments} />
</MockedProvider>
)
// This will fail because the wrapper is of the MockedProvider
// instance, not the Assignments instance
expect(wrapper.state('assignments')).to.eql([...])
})
})
I've tried to find a way via Enzyme to access the component instance of a child rather than the root, which as far as I can tell is not supported. I've also been trying to find alternatives to needing the MockedProvider in these tests but have not found anything yet.
Has anyone figured out a workaround for this sort of situation, or is there a different approach I should be taking to deal with nested Apollo-wrapped components?
I've found a solution to my problem. The reason Apollo-wrapped descendants of mounted components were causing problems is that they would throw an error when trying to access this.context.client. Apollo's MockedProvider creates an Apollo client (or optionally uses one you provide) and makes it available to its children via context.
Turns out Enzyme's mount method allows you to specify the component's context. I had tried using that before, but didn't realize that I also needed to combine it with the childContextTypes for that context to be passed down to the mounted component's descendants. Using these Enzyme options avoids the need to use the MockProvider.
I'll demonstrate the solution based off the example provided in my original question:
import React from 'react'
import { mount } from 'enzyme'
// This is an Apollo client I configured in a separate module
// with a mocked network interface. I won't go into details on
// that here, but am happy to provide more details if someone asks
import mockedClient from 'test/mocked_client'
// This component has descendants that are wrapped in Apollo and
// thus need access to `this.context` provided by an Apollo provider
import Assignments from 'app/components/assignments
...
describe('<Assignments />', function() {
it('sets sorted assignments in initial state', function() {
const assignments = [...]
const wrapper = mount(
<Assignments assignments={assignments} />,
{
context: { client: mockedClient },
childContextTypes: {
client: React.PropTypes.object.isRequired
}
}
)
// This now passes!
expect(wrapper.state('assignments')).to.eql([...])
})
})
Hopefully this helps someone who finds themselves in a similar situation!