ReferenceError: 您试图在 Jest 环境被关闭后“导入”一个文件

我有一个组件,使用 Animated组件从本地反应。我开始编写一个测试用例来模拟组件的 onPress,它调用一个包含 Animated.timing的函数,以及 setState

运行 jest工作得很好,但是测试从未停止运行,而且我以前编写的一个不相关的测试用例现在似乎从未通过(以前通过过)。

运行 jest --watch,我得到这个错误:

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)

连接回复

Https://repl.it/repls/partialgrimymetadata

环境:

  • 操作系统: Linux 4.14
  • 节点: 6.14.2
  • 纱线: 1.7
  • Npm: 3.10.10
  • 守望者: 没有找到
  • Xcode: N/A
  • Android Studio: Not Found Android Studio: 没有找到
96680 次浏览

OK, found a solution.

Should use jest.useFakeTimers()

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

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.

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);
});
});
}

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

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);

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

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!

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

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

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.

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

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


connection.close();
});

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.

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.

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!

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.

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.

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;