There's a component which I am using the cache that I created in apollo, stored inside the client.
At some point, as a callback from an input, I do the following call:
const originalName = client.readQuery<NamesCached>({ query: NamesCached })!.names
this returns directly an array of objects.
However, I do not know how to test this in my component. My first assumption was to mock exclusively the client.readQuery to return me an array from my __fixtures__. That didn't work.
Here is my best shot to mock it:
it('filters the name in the search', () => {
jest.doMock('#/ApolloClient', () => ({
readQuery: jest.fn()
}))
const client = require('#/ApolloClient')
client.readQuery.mockImplementation(()=> Promise.resolve(names()))
const { getByPlaceholderText, queryByText } = renderIndexNames()
const input = getByPlaceholderText('Search…')
fireEvent.change(input, { target: { value: 'Second Name' } })
expect(queryByText('Title for a name')).not.toBeInTheDocument()
})
For this test I am using react-testing-library. This is my best shot so far...
jest.doMock('#/ApolloClient', () => ({
readQuery: jest.fn()
}))
const client = require('#/ApolloClient')
client.readQuery.mockImplementation(()=> Promise.resolve(names()))
It is important that relies on a single IT, not on the top since there are other tests that are using the client and works properly.
With That I could had mocked in the test when invokin client.readQuery() but then in the component itself goes back to its own original state
My goal
Find a way that the client.readQuery can be mocked and return the data I am looking for. I thought in mocking, but any other solution that can work for a single or group of tests( without all of them) would be more than welcome.
I tried as well mocking on the top, but then the others are failing and i couldn't reproduce to go back to the real implementation
Related
In react-testing-library you have to render your react component before executing some tests on its elements.
For several tests on the same component, should you avoid
rendering the component multiple times? Or do you have to render it in each
test()/it() block?
Should you select elements of the component (e.g. button) in each test()/it() block, or should you lift the selection, and select only once?
Does it have any impact on the execution time of the tests?
Is one of the approaches a best practice/antipattern?
Why does the last example fail?
For the basic component I have the following testing approaches:
function MyComponent() {
return (
<>
<button disabled>test</button>
<button disabled>another button</button>
</>
);
}
e.g.
describe("MyComponent", () => {
it("renders", async () => {
const { getByRole } = render(<MyComponent />);
const button = getByRole("button", { name: /test/i });
expect(button).toBeInTheDocument();
});
it("is disabled", async () => {
// repetetive render and select, should be avoided or adopted?
const { getByRole } = render(<MyComponent />);
const button = getByRole("button", { name: /test/i });
expect(button).toBeDisabled();
});
});
vs.
describe("MyComponent", () => {
const { getByRole } = render(<MyComponent />);
const button = getByRole("button", { name: /test/i });
it("renders", async () => {
expect(button).toBeInTheDocument();
});
it("is disabled", async () => {
expect(button).toBeDisabled();
});
});
I would expect the second approach to have a faster execution time since the component has to be rendered only once, but I don't know how to measure it and if it is an anti-pattern?
While it seems to be more DRY, if I add another toBeInTheDocument check, it fails.
Why is this the case?
describe("MyComponent", () => {
const { getByRole } = render(<MyComponent />);
const button = screen.getByRole("button", { name: /test/i });
const button2 = screen.getByRole("button", { name: /another button/i });
it("renders", async () => {
expect(button).toBeInTheDocument(); //ok
});
it("is disabled", async () => {
expect(button).toBeDisabled(); // ok
});
it("renders second button", async () => {
expect(button2).toBeInTheDocument(); // fails: element could not be found in the document
});
});
So this approach seems to be more error-prone!?
Each test should be as atomic as possible, meaning that it should not be using anything that other tests are also using and should run with a fresh state. So relating that to your examples, the first one would be the correct pattern.
When you have a test suite that contains sharable state between unit tests e.g. objects or environment variables, the test suite is very prone to errors. The reason for that is; if one of the unit tests happens to mutate one of the shared objects; all of the other unit tests will also be affected by this, causing them to exhibit unwanted behaviour. This can result in test failures where the code is technically correct or even set up landmines for future developers where the addition of new tests which are correct would still result in failures, hence causing major headaches in figuring out why this is happening.
The only exception to this rule would be immutable primitive variables (e.g. string, number, boolean with the use of const keyword) as tests will not be able to mutate them and they are useful for storing reusable ids, text etc.
Ofcourse, repeating the setup of each unit test can make them really clunky, that's why jest offers the beforeEach, beforeAll, afterEach and afterAll functions to extract the repeating logic. However, this opens up the vulnerability of shared state, so do be careful and make sure that all state is refreshed before any tests are kicked off. Ref.
For the last question as to why your last unit test in the last example is failing - it appears that you are using getByRole to look for button text. You should be using getByText instead. getByRole is used with role attributes (e.g. <button role="test">test</button>) which you don't seem to be using.
I've been working on an application that uses react-query for handling and caching responses to our backend. Got a lot of inspiration from the creator behind that framework to use custom hooks for handling backend states. In the beginning it was quite easy but later on the hooks started to be dependent to one and another. This resulted in some wierd behavior in the CI were some testcases failed due to heavy load on the servers. (note that backend is mocked with msw)
Custom hook containing multiple custom hooks:
const useFooMutation = () => {
const cache = useQueryClient();
const { types } = useTypes(); // async call to backend
const { user } = useAuth(); // async call to backend
return useMutation(['key'], mutationFunction, {
onSuccess: async (data, variables) => {
// Do cache handling by using values types and user
},
});
};
Test case:
test('useFooMutation should foo', async () => {
const { wrapper } = setupCache();
const { result, waitFor, waitForNextUpdate } = renderHook(() => useFooMutation(), { wrapper });
await waitForNextUpdate();
act(() => {
result.current.mutate({
values
});
});
await waitFor(() => result.current.isSuccess && !result.current.isLoading);
expect(result.current.isSuccess).toEqual(true);
expect(result.current.data).toEqual(fooMockData);
});
When I debugged the code I saw that sometimes the values from the hooks useTypes and useAuth was not yet fetched. The values from the other custom hooks are used when I do cache manipulation. So this results in some wierd behavior for some machines. For example running locally this runs ok most of the times but on a heavy loaded CI server, it results in crashes quite often.
What I've tried so far is to add a timeout before the mutation function in the test case on a timeout that seems to work, not a nice solution. And by using the waitForNextUpdate the test case works fine most of the times but not when the CI is loaded.
So I need a good way to find out when then the useFooMutation has gathered all values from the hooks its dependent on before I can call mutate()?
I am using Recoil for state management and have problem testing my selector.
It seems mocking a function inside a selector is not possible.
My code is similar to the code below:
const currentUserNameQuery = selector({
key: 'CurrentUserName',
get: async ({get}) => {
const response = await myDBQuery({
userID: get(currentUserIDState),
});
return response.name;
},
});
function CurrentUserInfo() {
const userName = useRecoilValue(currentUserNameQuery);
return <div>{userName}</div>;
}
Now, I want to mock the return value of myDBQuery but it seems I cannot do that when using selector. I was wondering if there is any way to mock returned result of a function inside selector.
How about moving the logic from get into a separate file? After that, you can mock this function.
I am trying to write unit tests for a React component - That looks something broadly as follows -
a handle to a db is passed to a component
The component also receives data as props (that will change) - perhaps this is something that could come through a Redux store.
Now what I do in my useEffect for this data change is - Write into indexDB (actually using the idb library) and then on success, I read some of this data and setup a local state using setData and use this state to render
Basically all of the above works - so there is no problem with that (It's perhaps not right, but works!). My challenge is how to write unit test for this code. I have looked at using async inside jest.
The code looks something like as follows -
const Component = ({db, data}) => {
useEffect(() => {
const saveDataToDB = (data, successCB) => {
const tx = db.transaction("store", "readwrite");
const transactions = [tx.store.add(data)];
transactions.push(tx.done);
Promise.all(transactions).then((res) => {
successCB();
});
const cb = () => {
// Do somethingl
};
saveDataToDb(data, cb);
};
}, [data]);
// Rendering part.
return (
<div id="component-div">
</div>
);
};
export default Component;
and I am trying to write the tests for this component using something like following
describe('Component Tests', () => {
it("Component Renders with Data", () => {
const wrapper = mount(<Component data={data} db={db}/>);
expect(wrapper.find("#component-div").toEqual(true);
});
});
May be - there's something wrong the way component is implemented, that makes unit testing challenging. But it works, so not sure in general about what's the strategy for unit testing components that could have a few promise chains to resolve?
react testing library has support for async APIs. They can be used to achieve to solve the problem mentioned above.
This would require converting the test cases to async test cases and then awaiting using right the async APIs as described above.
The original test cases mention enzyme APIs, instead of that react-testing-library APIs are to be used.
I find myself in situations with React/Redux apps where I require a lot of functions to transform data when it comes back from the server by using libaries like moment or jwtDecode. For example:
function mapDispatchToProps(dispatch) {
return {
getAllTokens: () => {
dispatch(getAllTokens());
}
};
}
On page load, I run this.props.getAllTokens() to bring back all tokens from the server stored in the database which I'd like to show on the page (for example).
The dispatch uses Redux Thunk to perform some operations on the data and send to the store:
export const getAllTokens = () => dispatch => {
apiGetAllUsers()
.then(tokens => {
let newFormat = tokens.message.map(token => {
if (token.refreshToken) {
let decoded = jwtDecode(token.refreshToken);
let expiresIn = moment.unix(decoded.exp).fromNow();
return {
refreshToken: token.refreshToken,
email: token.email,
expiresIn
};
}
});
dispatch(adminTokensReceived(newFormat));
})
};
This function getAllTokens is in another file to help the component keep lean. This approach works fine for Redux, but when it comes to using Apollo instead, how would I update the data before adding it back to the component props?
I have came across this link which shows how to update data from a query, but I don't see many examples using it, so I wondered if I'm missing something fundamental?
So my questions is, what is the best way to use helper functions as I've described above?
Thanks