111

I have a component that makes use of Animated component from react native. I started writing a test case to simulate onPress of a component, which calls a function that has Animated.timing in it, and setState.

running jest works fine, but the tests never stops running, and one unrelated test case that I've written before never seem to pass now (which passed before).

running jest --watch, I get this error:

ReferenceError: You are trying to `import` a file after the Jest environment has been torn down.

      at Function.bezier (node_modules/react-native/Libraries/Animated/src/Easing.js:113:21)
      at ease (node_modules/react-native/Libraries/Animated/src/Easing.js:34:24)
      at TimingAnimation._easing (node_modules/react-native/Libraries/Animated/src/Easing.js:133:18)
      at TimingAnimation.onUpdate (node_modules/react-native/Libraries/Animated/src/animations/TimingAnimation.js:107:45)

 RUNS  src/__tests__/SlideDownMenu.test.js

/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:114
      return _bezier(x1, y1, x2, y2);
             ^
TypeError: _bezier is not a function
    at Function.bezier (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:224:12)
    at ease (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:94:21)
    at TimingAnimation._easing (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/Easing.js:255:16)
    at TimingAnimation.onUpdate (/home/nrion/Desktop/mobile-ui/PriceInsight_app/node_modules/react-native/Libraries/Animated/src/animations/TimingAnimation.js:138:14)
    at ontimeout (timers.js:386:11)
    at tryOnTimeout (timers.js:250:5)
    at Timer.listOnTimeout (timers.js:214:5)

Link to repl

https://repl.it/repls/PartialGrimyMetadata

Environment:

  • OS: Linux 4.14
  • Node: 6.14.2
  • Yarn: 1.7.0
  • npm: 3.10.10
  • Watchman: Not Found
  • Xcode: N/A
  • Android Studio: Not Found
skyboyer
  • 22,209
  • 7
  • 57
  • 64
nrion
  • 4,258
  • 4
  • 17
  • 33

22 Answers22

167

OK, found a solution.

Should use jest.useFakeTimers()

Note: Put the code above just after import section in your test file.

Shiva
  • 11,485
  • 2
  • 67
  • 84
nrion
  • 4,258
  • 4
  • 17
  • 33
  • 41
    where do we use this? – dopatraman Jun 14 '19 at 23:51
  • 3
    @dopatraman I used it in my test file after the imports. (Is that correct? It worked.) But see below from TARJU also about its use. – Nabha Cosley Sep 04 '19 at 23:20
  • 5
    if u have a src\__tests__\jestSetup.ts better to set it there, it will work for every test. – netalex Nov 26 '20 at 09:12
  • 1
    unfornately didn't work for me, i used websocket conennection in a tested file which keeps failing – iLyas Jun 17 '21 at 04:48
  • See the documentation here: https://jestjs.io/docs/timer-mocks – pipedreambomb Jul 02 '21 at 14:28
  • √ renders correctly (400 ms) console.warn Animated: `useNativeDriver` is not supported because the native animated module is missing. Falling back to JS-based animation. To resolve this, add `RCTAnimation` module to this app, or remove `useNativeDriver`. Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md – cmcodes Mar 09 '22 at 11:46
52

jest.useFakeTimers()

With above it's extremely important to understand this

jest.useFakeTimers() mocks out setTimeout and other timer functions with mock functions.

If running multiple tests inside of one file or describe block, jest.useFakeTimers(); can be called before each test manually or with a setup function such as beforeEach.

Not doing so will result in the internal usage counter not being reset.

TARJU
  • 1,785
  • 1
  • 14
  • 16
34

I would like to contribute to the answer.

Well let's see the error message

ReferenceError: You are trying to 'import' a file after the Jest environment has been torn down.

Torn down means: Jest already finished running and some part of your code is trying to execute after jest has already finished running the test. This is pretty common on Javascript due to its asynchronous nature.

Sometimes it happens after a promise callback was executed. For example:

import { someProcess } from 'a-library'

task.job().then(result => {
    someProcess(result)
})

In the example above, the code imports someProcess from a-library.

If the method job from the task object takes longer than the jest execution, its callback (then() invocation) will run outside jest because jest has already finished running the test. Therefore when someProcess gets executed it will be loaded from a-library so jest will complain that you are trying to load a library after jest has been torn down.

The answer marked as the solution is partially right because calling jest.useFakeTimers() will prevent your code to wait the n seconds you supposed to wait when calling setTime or similar, making your code artificially synchronous.

Making your test await those method calls would help you understand better where the error is being introduced.

The code that worked for me was

describe("Some test set", () => {
  let heavyWorkingService = library.workingService();

   // We are marking the test function call as async
   test(("Name of the test"), async  () => {

     // we are awaiting heavyWorkingService to finish its job
     await heavyWorkingService("some data")
   })
})

In my real scenario, my code was getting data from firebase and because I'm not using mocks for this test, it was trying to import firebase after the .get() data read returned. Marking the test as async and calling the method with the await keyword solved my problem

Of course there are a lot of ways to deal with this error, just wanted you to know the true reason behind this error so you can work in the right solution for your tests!

HudsonGraeme
  • 399
  • 3
  • 10
Juan Amador
  • 507
  • 4
  • 7
32

Add "testEnvironment": "jsdom" into jest key in package.json or jest.config.js

"jest": {
  "testEnvironment": "jsdom",
  "preset": "react-native",
   ...

taken from: https://stackoverflow.com/a/64567257/728287

Gianfranco P.
  • 10,049
  • 6
  • 51
  • 68
23

I am using @testing-library/react-native, so what I did is to cleanup in the setup file.

// jest.setup.js
import { cleanup } from '@testing-library/react-native';

afterEach(cleanup);
Gianfranco P.
  • 10,049
  • 6
  • 51
  • 68
Second Son
  • 1,481
  • 1
  • 15
  • 26
7

add the below lines at the very end of the jest test file, after all, tests are written.

afterAll(() => { 
  mongoose.connection.close()
})

and also just run a single test file at a time, for example,

> jest --verbose --runInBand -- filename.test.js

jdk
  • 451
  • 1
  • 6
  • 18
6

None of above works for me. My solution is to add to jest-setup.js file this code:

import { Animated } from 'react-native';

Animated.timing = () => ({
    start: () => jest.fn(),
});

Don't forget to include this file to your package.json jest configuration:

...
  "jest": {
    ...,
    "setupFiles": [
      "<rootDir>/src/tests/jest-setup.js"
    ]
  },
...

I hope it will help anybody

Ruslan Valeev
  • 1,529
  • 13
  • 24
4

jest.useFakeTimers was not an option for me since it brakes the execution of other libraries I am using.

My workaround was to simply add a delay after each test, so that any async operation has time to complete before Jest environment is torn down:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

describe('Test suite', () => {
  
  // Give time to any async operation to complete after each test
  afterEach(async () => {
    await sleep(2000);
  });

  // Also close any pending connection (or related) if any
  afterAll(async () => {
    await closeConnections();
  });

  it('My test', async () => {
    // Test...
  });
});

Of course the delay time must be tuned for your application.

revy
  • 3,945
  • 7
  • 40
  • 85
4

Add this into your jest.config.js

 timers: "fake",

This helps me to resolve this problem.

My jest.config.js file now looks this way

module.exports = {
  preset: "react-native",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
  transformIgnorePatterns: [
    "node_modules/(?!(@react-native|react-native|react-native-vector-icons)/)",
  ],
  timers: "fake",
};

In the latest versions of Jest use this one

import type { Config } from "jest";

const config: Config = {
  preset: "react-native",
  moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"],
  testPathIgnorePatterns: [
    "/node_modules/",
    "<rootDir>/template",
    "Libraries/Renderer",
    "RNTester/e2e",
  ],
  transformIgnorePatterns: [
    "node_modules/(?!(@react-native|react-native|react-native-vector-icons)/)",
  ],
  fakeTimers: {
    enableGlobally: true,
  },
  verbose: true,
};

export default config;
Ann
  • 772
  • 1
  • 9
  • 17
1

I was getting this issue while testing Apollo with react-native-testing-library.

In this case there were two queries, in a parent and child component. The parent query needed to resolve before the child rendered and fired its query.

The solution was to run the wait() function twice rather than just once. I assume the double-fire is necessary to ensure both queries run. The error message is very opaque though.

test("...", () => {
  const rr = render(...);
  await wait();
  await wait(); // Child query must also run
  expect(...);
}

// Wait a tick so the query runs
// https://www.apollographql.com/docs/react/development-testing/testing/#testing-final-state
export async function wait(ms = 0) {
  await act(async () => {
    return new Promise(resolve => {
      setTimeout(resolve, ms);
    });
  });
}
Freewalker
  • 6,329
  • 4
  • 51
  • 70
1

I'm using @testing-library/react-native. On my test case, I need to call render(<MyComponent/>) twice so I needed to add afterEach(cleanup) that way it fixed the issue.

Stukz
  • 51
  • 3
  • 3
    As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 16 '21 at 05:56
1

In my case i was using typeorm and put that code inside my jest.setup.ts

afterAll(async () => {
 const connection = getConnection();

 connection.close();
});
1

Just to add my bit to the discussion... Double-check if you have any asynchronous code in your test case.

My case was basically a lack of attention, see:

it('should not allow click', async () => {
    const page = await newE2EPage();
    await page.setContent(`<my-button disabled></my-button>`);

    const component = await page.find('my-button');

    // WRONG ! ! ! (since click() is async)
    component.click();

    // ✅ Correct
    await component.click()

    ...
  });

The error message isn't that straightforward to understand (at least it wasn't for me). I hope this helps someone!

Zimah
  • 49
  • 4
0

I tried adding jest.useFakeTimers() inside my beforeEach() method but it didn't resolve the issue for me.

This jest.mock() call above my test cases was the culprit for me:

jest.mock('axios', () => ({
    post: () => Promise.resolve({ data: 'data' }),
}));

Removing it resolved my issue.

Hope this helps someone.

flyingfishcattle
  • 1,817
  • 3
  • 14
  • 25
0

Adding "testEnvironment": "jsdom" in package.json didn't helped in my case. Then I added in the test file under all imports:

jest.useFakeTimers();

and inside describe but above the first test:

  beforeEach(() => {
    jest.resetAllMocks();
  });

and that solved the problem for me.

Kiril Dobrev
  • 839
  • 1
  • 8
  • 12
0

IN THIS CASE THE ERROR IS SELF EXPLANATORY:

Maybe a bit late to the party but I just want to help whoever is still struggling with this. As the error says, the problem is that some part of your code is trying to import a module after the test has finished. Check the following code:

it('should do something', () => {
   someAsyncFuntion(); // We arent awaiting this function so the test ends before execution
});

const someAsyncFunction = async () => {
  const {getByPlaceholderText} = render(<SomeComponent />)

  getByPlaceholderText('Some placeholder text')
}

With this code the error will be fired, but why? If we check the code for getByPlaceholderText we will see that it is dynamically importing the TextInput component:

const getTextInputNodeByPlaceholderText = (node, placeholder, options = {}) => {
  try {
    const {
      TextInput
    } = require('react-native'); // <--- Here we have our import

    const {
      exact,
      normalizer
    } = options;
    return (0, _filterNodeByType.filterNodeByType)(node, TextInput) && (0, _matches.matches)(placeholder, node.props.placeholder, normalizer, exact);
  } catch (error) {
    throw (0, _errors.createLibraryNotSupportedError)(error);
  }
};

So as the error says, we are trying to import a module after the test has finished.

Toni Bardina Comas
  • 1,670
  • 1
  • 5
  • 15
0

My project is with ReactNative and TypeScipt

and my Jest config by default is in package.json.

To solve this issue i just had to add "timers": "fake"

"jest": {
"timers": "fake",
"preset": "react-native",
"moduleFileExtensions": [
  "ts",
  "tsx",
  "js",
  "jsx",
  "json",
  "node"
]}

The reason why you need this was already explained:

Torn down means: Jest already finished running and some part of your code is trying to execute after jest has already finished running the test. This is pretty common on Javascript due to its asynchronous nature.

There's NO need to add "testEnvironment": "jsdom". As people say above; has no sense with ReactNative.

0

Using jest.useFakeTimers() on top of code after imports, did not work for me.

I found the below solution here now at least, I do not see the error now but a warning message appeared at the end saying..

A worker process has failed to exit gracefully and has been force exited. This is likely caused by tests leaking due to improper teardown. Try running with --detectOpenHandles to find leaks. Active timers can also cause this, ensure that .unref() was called on them.

it('your test case', () => {...})

becomes:

it('your test case', async () => {...})

adding async removes the erroring out.

0

This is for NestJS developers.

I found this while I was testing, turns out I just needed to import some modules, you need to know that the Test module and the real module have to be in 1:1. I mean Daaaaa. Silly, but sometimes you can get clumsy when it's mostly a repeatable job.

Irfandy Jip
  • 1,308
  • 1
  • 18
  • 36
0

What worked for me was that I was starting a server that never finishes (open handle) i.e apollo graphql server. It is not the best practice to do this but what worked for me is wrapping the start of a server in a try catch. For example

async function startServers() {

    try {
        await createExpressApp()
        await RunHttpsServer();
        await startApollo();
        await onStartNeo();
        await startWeaviate();
    } catch (error) {
        console.log(error);
        return;
    }
}

tbh this is not the best way but if you know another please let me know as this gives the error

Jest did not exit one second after the test run has completed.

'This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue
Jonathan Coletti
  • 448
  • 4
  • 13
0

For me worked:

beforeEach(() => {
  jest.useFakeTimers({
    legacyFakeTimers: true,
  });
});

afterAll(() => {
  jest.useRealTimers();
});
Chaki_Black
  • 882
  • 3
  • 12
  • 30
-1

I used jest.useFakeTimers() in beforeEach() method inside test suit of that file

beforeEach(() => {
    jest.useFakeTimers();
  });

OR

use jest.useFakeTimers() on top of code after imports

راجہ مخلص
  • 1,177
  • 6
  • 15