I have some work in progress tests, this is working as expected:
describe("Parent", () => {
afterEach(() => {
cleanup();
jest.resetModules();
});
describe("Test 1", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
expect(wrapper.baseElement).toMatchSnapshot();
expect(wrapper.getByText("Apply").disabled).toBe(true);
});
});
describe("Test 2", () => {
test("1 ", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
console.log(wrapper.getByText("Apply").disabled);
expect(1).toBe(1);
});
});
});
However when I move the 2nd render function out of the test it errors:
describe("Parent", () => {
afterEach(() => {
cleanup();
jest.resetModules();
});
describe("Test 1", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
expect(wrapper.baseElement).toMatchSnapshot();
expect(wrapper.getByText("Apply").disabled).toBe(true);
});
});
describe("Test 2", () => {
const wrapper = render(
<MockProvider>
<MyComponent />
</MockProvider>
);
test("1 ", () => {
console.log(wrapper.getByText("Apply").disabled);
expect(1).toBe(1);
});
});
});
The error Im getting is
Found multiple elements with the text: Apply
I can see in the console that the component is being rendered twice, so I think the cleanup function must not be working properly in regards to the describe block. This is weird as we already have Enzyme tests and the setup and tear down works fine for those.
To understand this we need to understand a little bit about how Jest runs our tests and how React Testing Library renders our components.
Jest
Consider the code below and try to guess what the output will be:
describe('First describe', () => {
console.log('First describe');
it('First test', () => {
console.log('First test');
});
});
describe('Second describe', () => {
console.log('Second describe');
it('Second test', () => {
console.log('Second test');
});
});
Output (hover to see):
First describe
Second describe
First test
Second test
Note that all describe methods were initialised before the tests started to run.
This should already give you an idea of the problem, but let's now look at RTL.
React Testing Library
Consider the code below and try to guess what the DOM will look like in the console:
const Greeting = () => 'Hello world';
describe('First describe', () => {
const wrapper = render(<Greeting />);
it('First test', () => {
console.log(wrapper.debug());
});
});
describe('Second describe', () => {
render(<Greeting />);
});
Output (hover to see):
<body>
<div>Hello world</div>
<div>Hello world</div>
</body>
When we don't specify a base element to the render function, it always uses the same document.body
The <div> elements wrapping Hello world are added by RTL when we don't specify a custom container.
All RTL queries are bound to the base element by default--document.body in this case.
Therefore,
getByText('Hello world'); // will find two elements and throw
This is how the code in RTL looks like for the render function. (semi-pseudo code)
if(!baseElement) {
baseElement = document.body // body will be shared across renders
}
if(!container) {
baseElement.appendChild(document.createElement('div')) // wraps our component
}
ReactDOM.render(component, container)
return { container, baseElement, ...getQueriesForElement(baseElement) }
To solve this issue
Do one of the following:
call render inside the it or test methods
specify the container in the queries
specify a diferent base element for each render
I was running into a separate problem, but in the midst found the likely answer to your problem.
Jest describe blocks run in sequence before any of the tests run. Source
So you should basically never execute code in a describe block aside from variable scoping. If you need to set a variable or render a mock that is used across multiple tests, put it in a lifecycle method like beforeAll or beforeEach.
Another Quick fix for this could be wrapping your component with "React.Fragment" before passing it to render()
test ('should find text in <Component />', () => {
const {getByText} = render (<><Component /></>)
const divElement = getByText (/Find Text/i)
expect (divElement).toBeInTheDocument ()
})
Related
I'm trying to test a component that loads data asynchronously when mounted. The component works as expected, it's just the test that's giving me issues. The component's async loadData() function hangs at await axios.get() while jest test runner is in the component.vm.$nextTick(). As a result, the checks in the $nextTick loop never pass.
Immediately after the $nextTick loop times out, the component's await statement completes and the component renders itself. axios is mocked, so it should resolve really fast. If I remove the await and just fill in a constant instead, the entire thing executes as expected.
I'm guessing that $nextTick loop is not asynchronous and it's consuming the thread, even though this is the recommended way of testing asynchronous stuff. The problem is, I don't have an onclick async handler to await: this method is called from onMount.
Unfortunately, I don't know how to make a jsFiddle of this one, so I hope this will be enough:
my component (the relevant parts)
export default {
data() { return { content: '' }; },
mounted() { this.loadDoc() }
methods: {
async loadDoc() {
const res = await axios.get('some url'); // <-- this is the line that hangs until timeout
// const res = { data: 'test data'}; // this would test just fine
this.content = res.data;
}
}
}
and my component.spec.js:
jest.mock('axios', () => ({
get: async (url) => {
return { data: 'test data' };
}
};
describe('my super test', () => {
it('renders', (done) => {
const doc = shallowMount(myComponent);
doc.vm.$nextTick(() => {
expect(doc.html()).toContain('test data'); // <-- this never matches
done();
});
});
});
I would delete, but I just spent quite some hours for something that was suggested in the docs, but not explained that it's the only way... I'm hoping somebody else finds this useful.
Using flush-promises package instead of $nextTick loop immediately "fixed" the problem
Code sample (rework of above):
describe('my super test', () => {
it('renders', async() => {
const doc = shallowMount(myComponent);
await flushPromises();
expect(doc.html()).toContain('test data'); // <-- now it works
});
});
I am having problems loading filenames into jest.each asynchronously.
My code:
let files: string[][]
function getFilesWorking() {
files = [["test1"], ["test2"]]
}
async function getFilesAsync() {
files = await Promise.resolve([["test1"], ["test2"]])
}
beforeAll(() => {
console.log("before")
})
describe.only("Name of the group", () => {
getFilesAsync()
test.each(files)("runs", f => {})
})
beforeAll is executed before each test but NOT before initialization of test.each, so I end up with undefined.
How can I load files before using test.each?
You can pass an async callback to beforeAll and await getFilesAsync within it
beforeAll(async () => {
await getFilesAsync();
})
As of Jest 28.1.3 and prior, this is not possible. There is an open issue documenting this behavior.
The best thing you can do for now is put your tests in a regular it() test and do a deep value comparison:
it('tests an array of cases', async () => {
const data = await getSomeAsyncData()
const expectedData = [ ... ]
expect(data).toEqual(expectedData)
})
You can use beforeEach to set up code that will run prior to tests for any given scope, https://jestjs.io/docs/setup-teardown:
beforeEach(() => {
console.log('before every test');
});
describe.only(('Name of the group') => {
beforeEach(() => {
console.log('before tests in this describe block');
})
})
Jest is only going to run the tests in your describe.only block. If you want to use beforeEach in other blocks and run those tests as well, change describe.only to describe.
(Edit: I know this is a year late, I'm just trying to look for a similar problem/solution set and thought I could answer this.)
I am using jest for unit testing in my node express application,please excuse , because i am new to all this
in my abc.test.js
const s3Helper = require('../../../../../lib/s3_helper');
beforeEach(async () => {
s3Helper.uploadBufferToS3 = jest.fn(() => true);
});
describe('test1', () => {
it('test1', async () => {
expect(s3Helper.uploadBufferToS3)
.toHaveBeenCalled();
});
});
describe('test2', () => {
it('test2', async () => {
expect(s3Helper.uploadBufferToS3)
.toHaveBeenCalled();
});
});
so when i run this test file in test1 it returns that test is passed, however in test2 it returns expected >=1 returned 0.
since i am mocking it beforeEach i expect it should return 1 for each describe block
I would like to mock imported modules while keeping my unit tests independent of each other.
Setting resetMocks:true in my Jest config file means that behaviour set up using module factory mocking is lost (issue). Setting up module mocks in any other way doesn't work (issue).
Changing to resetMocks:false couples the unit tests and makes the order they are executed matter, which goes against unit testing best practices.
I have tried calling jest.mock('./a', () => {/* implementation */}) inside beforeEach() and at the top of test(). I have also tried to use a reference to a jest.fn() inside the module factory mock and then call .mockImplementation() on that reference.
Minimum demonstration:
// greeter.ts
export class Greeter {
sayHello(): string {
return 'hello world!';
}
}
// module-mocking.spec.ts
import { Greeter } from "./greeter";
jest.mock('./greeter', () => ({
Greeter: jest.fn(() => ({ sayHello: () => 'goodbye world!' }))
}));
test('mocked module behaviour should exist', () => {
const result = new Greeter().sayHello();
expect(result).toEqual('goodbye world!');
});
This test fails with the error:
TypeError: (intermediate value).sayHello is not a function
Moving the jest.mock() inside beforeEach() or into test() results in:
Expected: "goodbye world!" Received: "hello world!"
Edit
I managed to work around this by using require instead of import. The question still remains for ES6 imports.
// module-mocking.spec.ts
const greeter = require("./greeter");
let mockGreeter: any;
beforeEach(() => {
mockGreeter = { sayHello: () => 'goodbye world!' };
greeter.Greeter = jest.fn(() => mockGreeter);
});
test('mocked module behaviour should exist', () => {
const result = new Greeter().sayHello();
expect(result).toEqual('goodbye world!');
});
So I'm using mocha and node to test some apis. I have a test that goes
import { describe, before, it, xit } from 'mocha';
describe('test my scenarios dude', () => {
before('do all my pre-test stuff', () => {
const blah = blah;
});
it('tests my really useful test', () => {
const testName = this.test.ctx.currentTest.fullTitle();
});
});
The 'this' is undefined though. How can I get the test name?
https://mochajs.org/#arrow-functions
as docs says Passing arrow functions (“lambdas”) to Mocha is discouraged
use function instead
describe('test my scenarios dude', function() {
before('do all my pre-test stuff', function() {
const blah = blah;
});
it('tests my really useful test', function() {
const testName = this.test.ctx.currentTest.fullTitle();
});
});
also you can read more about arrow functions here. they don't have this