use-between
use-between copied to clipboard
Jest Testing with use-between hooks
Anyone Jest testing with use-between hooks? Let me give you a shortened example code...
export const MyDialog = () => {
// shared use between hook set in another component
const { displayAddAnalysis, setDisplayAddAnalysis } = useMyHook();
return (
<Dialog id="dlg-new-planned-analysis"
header={`New Planned Analysis`}
visible={displayAddAnalysis}
onHide={onHide}>
)
}
I need to set the displayAddAnalysis use-between hook value to true for my Jest test.
Here is the only way we could figure out to set it because use-between must be used in a Component is to create a fake <testComponent> but some on our dev team think this feels wrong. Any thoughts on the best way to test use-between hooks in Jest?
function getDialog() {
//Test component exists so that the setMyHook can be switched to true.
//Otherwise, the Dialog component will not exist to test
function TestComponent() {
const { setDisplayAddAnalysis } = usePlannedAnalysisHook();
setDisplayAddAnalysis(true);
return null;
}
render(<TestComponent />);
return render(<MyDialog />);
}
test("Making sure error messages do not show on component loading", async () => {
//Arrange
const Dialog = getDialog();
// Act
const study = await Dialog.findByTestId("studyError");
//Assert
expect(study.textContent).toMatch("");
});
}
Thanks for using the library!
I made API for testing, welcome to try it!
// ./shared-counter.test.js
import { clear, get, act, on } from 'use-between'
import { useCounter } from './shared-counter'
// Clean up after each test if necessary
afterEach(() => clear())
// Test example
it('It works', async () => {
expect(get(useCounter).count).toBe(0)
// It's good practice to use "act" for modifying operations
await act(() => {
get(useCounter).inc()
})
expect(get(useCounter).count).toBe(1)
await act(() => {
get(useCounter).inc()
})
expect(get(useCounter).count).toBe(2)
})
it('It works with spy', async () => {
const spy = jest.fn()
// Subscribe to a state change
on(useCounter, (state) => spy(state.count))
await act(() => {
get(useCounter).inc()
})
expect(spy).toBeCalledWith(1)
await act(() => {
get(useCounter).dec()
})
expect(spy).toHaveBeenLastCalledWith(0)
})
github.com/betula/use-between/blob/master/tests/example.test.ts
// ./shared-counter.js
import { useState, useCallback } from 'react'
export const useCounter = () => {
const [count, setCount] = useState(0)
const inc = useCallback(() => setCount((c) => c + 1), [])
const dec = useCallback(() => setCount((c) => c - 1), [])
return {
count,
inc,
dec
}
}
Happy Coding!
very cool! This is a welcome addition for people Jest testing.
Will this be in the 1.0.2 release?
Thanks! Implemented in today's release 1.1.0 You can try it out.
I will give a shot tomorrow at the office. Thanks for the quick turnaround! I will let you know how it goes.
Hey I just began using the updated API for Jest testing, and everything is working as expected, minus Jest complaining in the logs.
Warning: An update to PlannedAnalysisDialog inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at PlannedAnalysisDialog (...PlannedAnalysisDialog.tsx:33:120)
A sample test in the suite:
it("A valid type should leave the error message blank upon submit", async () => {
await act(() => {get(usePlannedAnalysisHook).setDisplayAddAnalysis(true)})
const dialog = render(<PlannedAnalysisDialog />);
const typeF = dialog.getAllByText("Select a type");
const button = await dialog.findByTestId("submitButton");
fireEvent.change(typeF[0], { target: { value: { name: "Integrated", code: "Integrated" } } });
act(() => {button.click();})
const studyE = await dialog.findByTestId("typeError");
expect(studyE.textContent===null);
});
The log error doesn't point to anything in the test directly though, it points to the usage of the use-between hook in the component being tested.
@Abielf can you zip your test.ts file and then attach it to your ticket so he can see your full test?
Hello @Abielf!
Okay. With the "act" questions arose, I removed it from "use-between" (1.2.1).
Now we need to use "act" from "@testing-library/react" only where we need it.
The "act" is exactly what is needed in those places where the warnings "was not wrapped in act(...)" appear.
There is no additional "act" for "use-between" operations anymore.
import { get, useBetween, clear, mock } from 'use-between'
import { act, render } from '@testing-library/react'
afterEach(clear)
it('It works', async () => {
expect(get(useCounter).count).toBe(0)
// Without act
get(useCounter).inc()
// Check result
expect(get(useCounter).count).toBe(1)
get(useCounter).inc()
expect(get(useCounter).count).toBe(2)
})
it('It works with testing-library render component', async () => {
const Counter = () => {
const { count } = useBetween(useCounter)
return <i data-testid="count">{count}</i>
}
const el = render(<Counter />)
expect((await el.findByTestId('count')).textContent).toBe('0')
// You should use "act" from @testing-library/react
// otherwise there will be a warning.
act(() => {
get(useCounter).dec()
})
expect((await el.findByTestId('count')).textContent).toBe('-1')
})
I also have great news! I made a "mock" function for you!
it('It works with testing-library render component with mock', async () => {
mock(useCounter, { count: 10 })
const el = render(<Counter />)
expect((await el.findByTestId('count')).textContent).toBe('10')
// You should use "act" from @testing-library/react
// otherwise there will be a warning.
// Because here we are updating the state of the mock.
act(() => {
mock(useCounter, { count: 15 })
})
expect((await el.findByTestId('count')).textContent).toBe('15')
})
Try It Out and Happy Coding!
You rock @betula we will let you know how it goes.
@betula We confirmed these changes now make it all work perfectly. You are awesome!
Many thanks! It was great teamwork with you!😊👍