testing-library / dom-testing-library

🐙 Simple and complete DOM testing utilities that encourage good testing practices.
https://testing-library.com/dom
MIT License
3.26k stars 467 forks source link

unexpected behaviour redux test - Not checking properly the changes of the state #1095

Closed pol-sotos closed 2 years ago

pol-sotos commented 2 years ago

I am testing a hook that internally dispatch an action to fetch data and then it returns the value using the selector.

The state change in case of success in the following way: {inProgress: false, error: false, data: null} --> {inProgress: true, error: false, data: null} --> {inProgress: false, error: false, data: Object}.

To test the hook I am using a dummy component. In this component I track the value of the variables inProgress, error and data.

The test is failing because the first expect is triggered when the inProgress variable is already true.

I show the code below to be better understood.

Relevant code or config:

This is the hook:

export default function useGetRecommendedActionsSummarySelector() {
  const dispatch = useAppDispatch();
  useEffect(() => {
    dispatch(fetchRecommendedActionsSummary());
  }, [dispatch]);
  const recommendedActionsSummary: AsyncState<RecommendedActionsSummary | null> = useAppSelector(
    (state: RootState) => state.recommendedActionsSummary
  );
  return {
    recommendedActionsSummary
  };
}

This is the dummy component that tracks inProgress variable

const TestComponent = () => {
  const {
    recommendedActionsSummary
  }: {
    recommendedActionsSummary: AsyncState<RecommendedActionsSummary | null>;
  } = useGetRecommendedActionsSummarySelector();
 //Console.log to check when the value changes
  console.log(recommendedActionsSummary.inProgress.toString())
  return <span>{recommendedActionsSummary.inProgress.toString()}</span>;
};

This is the test that check InProgress variable

describe("useGetRecommendedActionsSummary", () => {
  it("Should in Progress be false, then true, then false after api returns response", async () => {
    render(
      <CoreProvider>
        <TestComponent />
      </CoreProvider>
    );
    console.log("expect1");
    // should show inProgress false when the component renders first time
    expect(screen.getByText(/false/i)).toBeInTheDocument();
    console.log("expect2");
    // after a while, it should now show that it is fetching the data
    expect(await screen.findByText(/true/i)).toBeInTheDocument();
    console.log("expect3");
    // after some time, the data should be received
    expect(await screen.findByText(/false/i)).toBeInTheDocument();
  });
});

So when I run the test I see printed in the console the following:

false true expect1

When it chekcs expect1 the test fails because inProgress is already true.

The order should be:

false expect1 true expect2 false expect 3

I have prepared the following sandbox so you can check it on your own:

https://codesandbox.io/s/n4s74

What you did:

What happened:

Test fails due to an unexpect ordering ofthe expects.

Reproduction:

To reproduce the issue you can check this sandbox: https://codesandbox.io/s/n4s74

Problem description:

Not possible to test a hook due to an erroneus behaviour

Suggested solution:

pol-sotos commented 2 years ago

I've run into a project thah doesn't check the initial state. It goes directly to loading=true;

The app fetchs some data when the component mounts.

https://github.com/rbrtsmith/react-tdd

If you clone the project you will see that in the in the first test it goes directly to test the component when the loading is true. If you test the initial state it fails as it happens to me.

eps1lon commented 2 years ago

Support requests filed as GitHub issues often go unanswered. We want you to find the answer you're looking for, so we suggest you check out our community discord and see if someone is able to help you.