Skip to content
This repository was archived by the owner on Jul 30, 2020. It is now read-only.

Warning: An update to Example inside a test was not wrapped in act(...). #25

Closed
lewie9021 opened this issue May 29, 2019 · 8 comments · Fixed by #50
Closed

Warning: An update to Example inside a test was not wrapped in act(...). #25

lewie9021 opened this issue May 29, 2019 · 8 comments · Fixed by #50
Labels

Comments

@lewie9021
Copy link
Contributor

lewie9021 commented May 29, 2019

  • react-native or expo: react-native
  • native-testing-library version: 3.1.1
  • jest-preset: native-testing-library
  • react-native version: 0.59.2
  • node version: 10.13.0

Relevant code or config:

import * as React from "react";
import { Button, Text, TextInput, View } from "react-native";
import { fireEvent, render, wait } from "native-testing-library";

function Example() {
  const [name, setUser] = React.useState("");
  const [show, setShow] = React.useState(false);

  return (
    <View>
      <TextInput value={name} onChangeText={setUser} testID="input" />
      <Button
        title="Print Username"
        onPress={() => {
          // let"s pretend this is making a server request, so it"s async
          // (you"d want to mock this imaginary request in your unit tests)...
          setTimeout(() => {
            setShow(!show);
          }, Math.floor(Math.random() * 200));
        }}
      />
      {show && <Text testID="printed-username">{name}</Text>}
    </View>
  );
}

test("examples of some things", async () => {
  const { getByTestId, getByText, queryByTestId } = render(<Example />);
  const famousWomanInHistory = "Ada Lovelace";

  const input = getByTestId("input");
  fireEvent.changeText(input, famousWomanInHistory);

  const button = getByText("Print Username");
  fireEvent.press(button);

  await wait(() => expect(queryByTestId("printed-username")).toBeTruthy());

  expect(getByTestId("printed-username").props.children).toBe(famousWomanInHistory);
  expect(baseElement).toMatchSnapshot();
});

What you did:

Ran the example found within the documentation that demonstrates an async state update: https://www.native-testing-library.com/docs/example.

What happened:

I get a console.error message suggesting there is a problem with the tests:

console.error node_modules/react-native/Libraries/YellowBox/YellowBox.js:132
    Warning: An update to Example 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://fb.me/react-wrap-tests-with-act
        in Example
        in View (created by View)
        in View (at AppContainer.js:98)
        in View (created by View)
        in View (at AppContainer.js:115)
        in AppContainer (at src/index.js:24)

Reproduction:

See code snippet above.

Problem description:

Whenever I run the tests, I'm flooded with these console.error messages whenever I test components that make state changes after async logic such as network requests.

Suggested solution:

I feel this is a known issue, but I've not yet seen a ticket open for it on this project. I figured it would be helpful for others to reference and track the progress.

I've attempted to Google around to see if I can find a solution, but appears to be something fundamentally wrong with the underlying library that the *-testing-library family uses, react-test-renderer. This was highlighted in a react-testing-library issue and appears to have a fix on the way in React 16.9 once at least this umbrella issue is closed.

Below is my current workaround:

In my Jest config file, I have a reference to a custom setup script:

module.exports = {
  // ...
  preset: 'native-testing-library',
  setupFiles: [
    ...jestPreset.setupFiles,
    "<rootDir>/setupJest.js"
  ],
  // ...
};

In the custom setup script (setupJest.js), I have the following:

// Auto-mock YellowBox component.
jest.mock("YellowBox");

const mockConsoleMethod = (realConsoleMethod) => {
  const ignoredMessages = [
    "test was not wrapped in act(...)"
  ];

  return (message, ...args) => {
    const containsIgnoredMessage = ignoredMessages.some((ignoredMessage) => message.includes(ignoredMessage));

    if (!containsIgnoredMessage) {
      realConsoleMethod(message, ...args);
    }
  };
};

// Suppress console errors and warnings to avoid polluting output in tests.
console.warn = jest.fn(mockConsoleMethod(console.warn));
console.error = jest.fn(mockConsoleMethod(console.error));

Note: mockConsoleMethod is used to only suppress logs that contain "test was not wrapped in act(...)".

@bcarroll22
Copy link
Collaborator

I’m cool with leaving this open until a solution is available. Unfortunately for us in React Native it may take a little longer because we not only need React 16.9, but also a version of React Native that supports 16.9.

I don’t necessarily suggest mocking YellowBox completely, because YellowBox can show you actual warnings in your tests just like your app would show you, but i do recommend that you can ignore certain messages in YellowBox like this

So, for reference for future readers, this isn’t a bug with the testing library family of tools, we’re ready for it once it’s fixed, but let’s leave this open as a reference for anyone else who has this question in the future 👍 thanks for calling it out!

@lewie9021
Copy link
Contributor Author

Apologies for my late reply on this!

I'm really glad to hear that you're prepared for when the updates land for the relevant packages!

Thanks for the tip on the YellowBox module. I'd completely forgot about its static properties for configuring. In the end, I decided to use console.disableYellowBox as I'm under the assumption that any messages shown by the YellowBox component have been triggered via console.warn. As a result, I'm still able to see any warnings that aren't related to act in the terminal when running the tests.

@toumas
Copy link

toumas commented Jul 31, 2019

While @lewie9021 's fix fixes the Warning: An update to Example inside a test was not wrapped in act(...) error when running tests the test still fails for me. I think the problem is that the YellowBox is still being rendered and that causes toMatchSnapshot() to fail.

I tried to console.disableYellowBox = true; in setup file but then it's back to act(...) error.

Error log FAIL src/Example.test.js ✕ examples of some things (228ms)

● examples of some things

expect(received).toMatchSnapshot()

Snapshot name: `examples of some things 1`

- Snapshot
+ Received

@@ -31,274 +31,6 @@
        >
          Ada Lovelace
        </Text>
      </View>
    </View>
-   <View
-     style={
-       Object {
-         "bottom": 0,
-         "position": "absolute",
-         "width": "100%",
-       }
-     }
-   >
-     <View
-       pointerEvents="box-none"
-       style={
-         Object {
-           "bottom": "100%",
-           "flexDirection": "row",
-           "justifyContent": "flex-end",
-           "paddingBottom": 4,
-           "paddingEnd": 4,
-           "position": "absolute",
-           "width": "100%",
-         }
-       }
-     >
-       <TouchableWithoutFeedback
-         hitSlop={
-           Object {
-             "bottom": 4,
-             "left": 4,
-             "right": 4,
-             "top": 4,
-           }
-         }
-       >
-         <View
-           style={
-             Array [
-               Object {
-                 "backgroundColor": "rgba(250, 186, 48, 0.95)",
-               },
-               Object {
-                 "borderRadius": 14,
-                 "height": 28,
-                 "justifyContent": "center",
-                 "paddingHorizontal": 12,
-               },
-             ]
-           }
-         >
-           <Text
-             numberOfLines={1}
-             style={
-               Object {
-                 "color": "rgba(255, 255, 255, 1)",
-                 "fontSize": 12,
-                 "includeFontPadding": false,
-                 "lineHeight": 16,
-               }
-             }
-           >
-             Dismiss All
-           </Text>
-         </View>
-       </TouchableWithoutFeedback>
-     </View>
-     <ScrollView
-       data={
-         Array [
-           Object {
-             "category": "Warning: An update to %s 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://fb.me/react-wrap-tests-with-act%s",
-             "warnings": Array [
-               YellowBoxWarning {
-                 "message": Object {
-                   "content": "Warning: An update to "Example" 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://fb.me/react-wrap-tests-with-act"
-     in Example (at Example.test.js:8)
-     in View (created by View)
-     in View (at AppContainer.js:98)
-     in View (created by View)
-     in View (at AppContainer.js:115)
-     in AppContainer (at src/index.js:24)"",
-                   "substitutions": Array [
-                     Object {
-                       "length": 9,
-                       "offset": 22,
-                     },
-                     Object {
-                       "length": 214,
-                       "offset": 377,
-                     },
-                   ],
-                 },
-                 "stack": Array [
-                   Object {
-                     "arguments": Array [],
-                     "column": 32,
-                     "file": "/Users/user.user/projects/my-app/node_modules/react-test-renderer/cjs/react-test-renderer.development.js",
-                     "lineNumber": 102,
-                     "methodName": "warningWithoutStack",
-                   },
-                   Object {
-                     "arguments": Array [],
-                     "column": 7,
-                     "file": "/Users/user.user/projects/my-app/node_modules/react-test-renderer/cjs/react-test-renderer.development.js",
-                     "lineNumber": 11770,
-                     "methodName": "warnIfNotCurrentlyBatchingInDev",
-                   },
-                   Object {
-                     "arguments": Array [],
-                     "column": 9,
-                     "file": "/Users/user.user/projects/my-app/node_modules/react-test-renderer/cjs/react-test-renderer.development.js",
-                     "lineNumber": 5630,
-                     "methodName": "dispatchAction",
-                   },
-                   Object {
-                     "arguments": Array [],
-                     "column": 11,
-                     "file": "/Users/user.user/projects/my-app/src/Example.js",
-                     "lineNumber": 17,
-                     "methodName": "Timeout._onTimeout",
-                   },
-                   Object {
-                     "arguments": Array [],
-                     "column": 11,
-                     "file": "timers.js",
-                     "lineNumber": 498,
-                     "methodName": "ontimeout",
-                   },
-                 ],
-                 "symbolicated": Object {
-                   "error": null,
-                   "stack": null,
-                   "status": "NONE",
-                 },
-               },
-             ],
-           },
-         ]
-       }
-       disableVirtualization={false}
-       horizontal={false}
-       initialNumToRender={10}
-       maxToRenderPerBatch={10}
-       numColumns={1}
-       onEndReachedThreshold={2}
-       removeClippedSubviews={false}
-       scrollEnabled={false}
-       scrollEventThrottle={50}
-       scrollsToTop={false}
-       stickyHeaderIndices={Array []}
-       style={
-         Object {
-           "height": 48.5,
-         }
-       }
-       updateCellsBatchingPeriod={50}
-       viewabilityConfigCallbackPairs={Array []}
-       windowSize={21}
-     >
-       <View
-         style={null}
-       >
-         <TouchableWithoutFeedback>
-           <View
-             style={
-               Array [
-                 Object {
-                   "backgroundColor": "rgba(250, 186, 48, 0.95)",
-                 },
-                 Object {
-                   "height": 48,
-                   "justifyContent": "center",
-                   "marginTop": 0.5,
-                   "paddingHorizontal": 12,
-                 },
-               ]
-             }
-           >
-             <View
-               style={
-                 Object {
-                   "alignItems": "flex-start",
-                   "flexDirection": "row",
-                 }
-               }
-             >
-               <Text
-                 numberOfLines={2}
-                 style={
-                   Object {
-                     "color": "rgba(255, 255, 255, 1)",
-                     "flex": 1,
-                     "fontSize": 14,
-                     "includeFontPadding": false,
-                     "lineHeight": 18,
-                   }
-                 }
-               >
-                 <Text>
-                   Warning: An update to 
-                 </Text>
-                 <Text
-                   style={
-                     Object {
-                       "color": "rgba(255, 255, 255, 0.6)",
-                     }
-                   }
-                 >
-                   "Example"
-                 </Text>
-                 <Text>
-                    inside a test was not wrapped in act(...).
- 
- When testing, code that causes React state updates should be wrapped into act(...):
- 
- act(() =&gt; {
-   /* 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://fb.me/react-wrap-tests-with-act
-                 </Text>
-                 <Text
-                   style={
-                     Object {
-                       "color": "rgba(255, 255, 255, 0.6)",
-                     }
-                   }
-                 >
-                   "
-     in Example (at Example.test.js:8)
-     in View (created by View)
-     in View (at AppContainer.js:98)
-     in View (created by View)
-     in View (at AppContainer.js:115)
-     in AppContainer (at src/index.js:24)"
-                 </Text>
-               </Text>
-             </View>
-           </View>
-         </TouchableWithoutFeedback>
-       </View>
-     </ScrollView>
-     <SafeAreaView
-       style={
-         Object {
-           "backgroundColor": "rgba(250, 186, 48, 0.95)",
-           "marginTop": 0.5,
-         }
-       }
-     />
-   </View>
  </View>

  21 |              famousWomanInHistory
  22 |      );
> 23 |      expect(baseElement).toMatchSnapshot();
     |                                   ^
  24 | });
  25 | 

  at Object._callee$ (src/Example.test.js:23:31)
  at tryCatch (node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:45:40)
  at Generator.invoke [as _invoke] (node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:271:22)
  at Generator.prototype.(anonymous function) [as next] (node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:97:21)
  at tryCatch (node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:45:40)
  at invoke (node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:135:20)
  at node_modules/@babel/runtime/node_modules/regenerator-runtime/runtime.js:145:13
  at tryCallOne (node_modules/promise/lib/core.js:37:12)
  at node_modules/promise/lib/core.js:123:15
  at flush (node_modules/asap/raw.js:50:29)

› 1 snapshot failed.
Snapshot Summary
› 1 snapshot failed from 1 test suite. Inspect your code changes or run npm test -- -u to update them.

Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 1 failed, 1 total
Time: 1.709s, estimated 2s
Ran all test suites.
npm ERR! Test failed. See above for more details.

@lewie9021
Copy link
Contributor Author

Hi @toumas,

Have you tried the approach of simply mocking the YellowBox module (jest.mock("YellowBox"))?

I'm personally of the opinion that it serves no purpose in the output as it's a component that's only present in development and therefore not a concern from the user’s point of view. As mentioned previously, my understanding of the YellowBox module is simply another output stream for console.warn calls.

@bcarroll22
Copy link
Collaborator

Your test has two main users: the end user and the developer.

It benefits the developer to know that there are YellowBox warnings, even though it doesn't benefit the end user because they shouldn't ever see it. My argument is that most of the time, YellowBox warnings should be relevant to you in your tests, unless you believe the YellowBox isn't useful in general (even during development). In that case, my suggestion would be to turn YellowBox off for your application because you don't find value in it or it isn't helping your team, otherwise you should pay attention to the YellowBox in your tests just like you would in your app.

All of that said, we can't help it in this case because the issue is with React, not this library. If in the meantime that means you mock the YellowBox in your tests, that's probably fine, but I'd suggest un-mocking it once React 16.9 is released and supported by React Native.

@bcarroll22
Copy link
Collaborator

This is partly addressed by #50 but you'll still get some errors.

Async act is only fully supported in React 16.9, and there's currently no version of React Native that officially supports React 16.9. As soon as a version does support it though, this will be good to go 👍

@bcarroll22
Copy link
Collaborator

🎉 This issue has been resolved in version 4.0.8 🎉

The release is available on:

Your semantic-release bot 📦🚀

@vmaark
Copy link

vmaark commented Feb 1, 2020

I'm getting the exact same issue with the following dependencies in my package.json:

    "@testing-library/jest-native": "^3.1.0",
    "@testing-library/react-hooks": "^3.2.1",
    "@testing-library/react-native": "^5.0.3",

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants