callstack / react-native-testing-library

🦉 Simple and complete React Native testing utilities that encourage good testing practices.
https://callstack.github.io/react-native-testing-library/
MIT License
3.05k stars 271 forks source link

Warning An update to ForwardRef inside a test was not wrapped in act(...) #1668

Open obaricisse opened 4 days ago

obaricisse commented 4 days ago

Describe the bug

How do I get rid of this warning an update to ForwardRef inside a test was not wrapped in act? I am already using act in my test but keep getting this warning even though test passes. see repro step.

Expected behavior

Steps to Reproduce

Here is an example component and the output.

import React, { PureComponent } from "react";
import { Button, Text, View } from "react-native";
import { connect } from "react-redux";
import { render, fireEvent, act } from "@testing-library/react-native";
import configureMockStore from "redux-mock-store";
import { Provider } from "react-redux";
import "core-js"; 

interface Props {
  count: number;
  incrementCount: () => void;
}

interface State {
  internalCount: number;
}

export class MyPureComponent extends PureComponent<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = {
      internalCount: props.count,
    };
  }

  componentDidUpdate(prevProps: Props) {
    if (prevProps.count !== this.props.count) {
      this.setState({ internalCount: this.props.count });
    }
  }

  render() {
    return (
      <View>
        <Text testID="internal-count">{this.state.internalCount}</Text>
        <Button
          disabled={this.props.count >= 1}
          title="Increment"
          testID="increment-button"
          onPress={this.props.incrementCount}
        />
      </View>
    );
  }
}

const mapStateToProps = (state: { count: number }) => ({
  count: state.count,
});

const mapDispatchToProps = (dispatch: any) => ({
  incrementCount: () => dispatch({ type: "INCREMENT" }),
});

export const ConnectedMyPureComponent = connect(
  mapStateToProps,
  mapDispatchToProps
)(MyPureComponent);

const mockStore = configureMockStore([]);

describe("MyPureComponent", () => {
  let store: any;

  beforeEach(() => {
    // Initial state for the store
    store = mockStore({
      count: 0, // Initial Redux state
    });

    store.dispatch = jest.fn(); // Mock dispatch
  });

  it("should update the internal state when the Redux count changes", () => {
    const { getByTestId, rerender } = render(
      <Provider store={store}>
        <ConnectedMyPureComponent />
      </Provider>
    );

    // Initial state should be 0
    expect(getByTestId("internal-count").props.children).toBe(0);

    // Update Redux state and rerender the component
    act(() => {
      store = mockStore({ count: 5 });
      rerender(
        <Provider store={store}>
          <ConnectedMyPureComponent />
        </Provider>
      );
    });

    // Now the internal state should be updated to 5
    expect(getByTestId("internal-count").props.children).toBe(5);
  });
});

Screenshots

image

Versions

12.7.2

image
mdjastrzebski commented 4 days ago

Our of curiosity, why are you externally triggering state update instead of triggering by a button press? That would be a recommended way in RNTL as it would resemble a real user interaction.

obaricisse commented 4 days ago

you need the button to be enabled in order to press it. the state update is to enable or disable the button. and it is triggered by a componentDidUpdate. Also this is just an example of how to repro the error. The use case makes more sense in my specific case.

mdjastrzebski commented 4 days ago

could you put it in a repro repo so I could reproduce it too?

obaricisse commented 4 days ago

Here is a code sandbox

let me know if you have any access issue.