Network Error Error: connect ECONNREFUSED 127.0.0.1:5000 after a certain number of tests in react-testing library - react-testing-library

I am testing an app with react-testing-library and I am using a mock service worker.
All my tests pass until the last one which gives this error in the title.
When testing only the part which gives error isolated (test.only), then it doesn't throw error.
The error points to localhost:5000 which is my data server (my app is running on 3000)
This is my last test which only works when running alone:
import { findByRole, getByRole, render, screen } from '#testing-library/react'
import userEvent from '#testing-library/user-event'
import Layout from '../../layout'
describe('tests for headers and content of the table', () => {
test('to check headers in inital state', async () => {
render(<Layout />)
const headerFirstCell = await screen.findByRole('columnheader', {name: /name/i, })
expect(headerFirstCell).toHaveTextContent('Name')
const headerSecondCell = await screen.findByRole('columnheader', {name: /courses/i,}) //second
expect(headerSecondCell).toHaveTextContent('Courses')
})
It is also strange that when I run only one part of the last code + all the rest, fex:
const headerFirstCell = await screen.findByRole('columnheader', {name: /name/i, })
expect(headerFirstCell).toHaveTextContent('Name')
Or the other part + all the rest:
const headerSecondCell = await screen.findByRole('columnheader', {name: /courses/i,}) //second
expect(headerSecondCell).toHaveTextContent('Courses')
Then all tests pass. It seems like any line of code I add after that point will make the error throw.
I know it might not be easy to see from this info...but I'm lost...Any clue??
Just in case this is my setupTests.js:
import '#testing-library/jest-dom'
import { server } from './mocks/server.js'
beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

Just was a typo in one of the server url addresses in my mock service worker handlers.
So when testing a part which gets requests to that url it threw an error sometimes, in case I place that function at the end, the tests finished before doing the get request to the wrong url. That's why it had that strange behaviour.

Related

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

How to enforce SLONIK to close open pools

THE PROBLEM
I have a NodeJs server where I'm using jest for testing. In case of integration tests i get a message after all tests where passing:
"Jest has detected the following X (15-20) open handles potentially keeping Jest from exiting"
I knew what this means, have already seen it when i was using Sequelize as ORM, but now I'm using Slonik.
I found this topic what was really useful:
https://github.com/gajus/slonik/issues/63
so when i set the idleTimeOut as advised it is solved.
test("foo", async () => {
const slonik = createPool(
`postgres://postgres:password#127.0.0.1:7002/postgres`,
{
maximumPoolSize: 1,
minimumPoolSize: 1,
idleTimeout: 1 // milliseconds!
}
);
await slonik.many(
sql`SELECT table_name FROM information_schema.tables WHERE table_schema='public';`
);
});
I tried to solve this problem from an other perspective. My ide was to close the connection in the afterAll block of jest. The test setup is:
const db = container.resolve(DbConnectionPool).connection;
let appServer;
let api;
beforeEach(() => {
appServer = app.listen(config.port);
api = supertest(appServer);
});
afterEach(async () => {
appServer.close();
await db.query(sql`TRUNCATE reports`);
});
What i have tried in the afterAll block:
afterAll(async () => {
await db.end()
});
It does not solves my problem as the documentation tells:
'Note: pool.end() does not terminate active connections/ transactions.'
I do not found anything about how to enforce the close of a pool until now.
So thought i can be tricky and i will close the connection using SQL:
afterAll(async () => {
await db.query(sql`DISCONNECT ALL`);
});
Does not work as well.
I still had an idea to play with. Slonik documentation tells, that the default idleTimeOut for a connection is 5000ms in default.
So i have tried to set a timeout in the afterAll block with 6000 ms, but i still get the warning from Jest.
So does anyone have any idea how to force-close the connection for my tests?

axios / jest - unabled to perform a call request (TypeError: Cannot read property 'then' of undefined)

I'm struggling to perform a test with jest concerning an axios api call
here is my API call, that works perfectly within my program
import axios from 'axios';
import crypto from 'crypto';
import { prop } from 'ramda';
const baseUrl = 'http://gateway.marvel.com:80';
const uri = '/v1/public/characters';
const charactersUrl = baseUrl + uri;
const timestamp = [Math.round(+new Date() / 1000)];
const privateApi = 'XXX';
const publicApi = 'XXX';
const concatenatedString = timestamp.concat(privateApi, publicApi).join('');
const hash = crypto.createHash('md5').update(`${concatenatedString}`).digest('hex');
const charactersApi = () =>
axios
.get(charactersUrl, {
params: {
ts: timestamp,
apikey: publicApi,
hash,
},
})
.then(prop('data'));
export default charactersApi;
When I'm trying to test it, that way:
import axiosMock from 'axios';
import charactersApi from '../marvelApi';
jest.mock('axios', () => ({
get: jest.fn(),
}));
describe('tools | marvelApi', () => {
const piece = { name: '3D-MAN' };
axiosMock.get.mockResolvedValueOnce({ data: piece });
it('should get the character', () => {
return charactersApi().then(elem => {
expect(elem.name).toEqual('3D-MAN');
});
});
});
I get the following message from jest
TypeError: Cannot read property 'then' of undefined
16 |
17 | const charactersApi = () =>
> 18 | axios
| ^
19 | .get(charactersUrl, {
20 | params: {
21 | ts: timestamp,
at charactersApi (src/tools/marvelApi.js:18:3)
at Object.<anonymous> (src/tools/tests/marvelApi.test.js:13:12)
What I have tried
A common error is to forget the return statement within the function that contain the request API, in my case it's done correctly (first piece of code -> charactersApi()) source1, source2
I also tried to return a Promise from the mocked Axios as I have seen on another SO ticket
jest.mock('axios', () => ({
get: jest.fn(() => Promise.resolve()),
}));
I think my axios mock is not correct, because the struggle comes from the test while the production version work well
Any thoughts ?
You can spy on the "axios.get" calls and resolve them to a fixed (mocked) value:
/**
* #jest-environment jsdom
*/
const axios = require('axios')
beforeAll(() => {
jest.spyOn(axios, 'get').mockImplementation()
})
afterAll(() => {
jest.restoreAllMocks()
})
it('returns the mocked response', async () => {
axios.get.mockResolvedValue({ data: 'foo' })
const res = await axios.get('https://api.github.com')
expect(res).toEqual({ data: 'foo' })
})
You shouldn't use jest.mock because it mocks a module that your imported code may be using. As far as I know, it doesn't affect the current module's imports (and you import axios as a part of your test).
Recommended solution
I strongly discourage you from spying/mocking axios directly. See my argumentation below.
You're mocking implementation details of axios. In other words, you take the axios.get function and throw it away, alongside any internal logic it may have, and replace it with a hard mock. This means your test no longer uses axios, instead it uses an emptied mocked shell of axios. This makes your test different from your actual code, which, in turn, decreases the confidence such a test gives you.
You're coupling your mocks with a specific request client (axios). Such an approach is not a long-term investment, as you're writing axios-specific mocks. You can't reuse such mocks for requests made by other clients (i.e. window.fetch, Apollo, etc.), because they have their own implementation details (i.e. window.fetch has no .get() to spy on), which only encourages you to write more implementation-specific logic in tests.
You can learn more about the disadvantages of direct mocking of request clients in the Stop mocking fetch article by Kent C. Dodds. It uses window.fetch mocks as an example, but you may replace it with ANY_REQUEST_CLIENT when reading.
I highly recommend using tools like Mock Service Worker (MSW) that will encourage you to write abstracted mocks that don't rely on any request clients (you can use them no matter how your tested code makes a request) and can even be reused across different testing levels (the same mocks for Jest, Storybook, or Cypress).
Here's how your test would look like with MSW:
import { rest } from 'msw'
import { setupServer } from 'msw/node'
import charactersApi from '../marvelApi';
const server = setupServer(
rest.get('http://gateway.marvel.com:80/v1/public/characters', (req, res, ctx) => {
return res(ctx.json({
data: {
name: '3D-MAN'
}
}))
})
)
beforeAll(() => server.listen()
afterAll(() => server.close())
describe('tools | marvelApi', () => {
it('should get the character', () => {
return charactersApi().then(elem => {
expect(elem.name).toEqual('3D-MAN')
})
})
})
Notice how there are no details about how the request is made, only which request to intercept and mock its response.
You can follow a detailed tutorial on how to Get started with MSW. There's also a great video on API mocking and what problems MSW solves.

NestJS: e2e testing using an axios-based client is not working as expected

I have a NestJS application that exposes some endpoints, and I have written a client application that I plan on releasing as an NPM package to work with the nest server. I am attempting to write end-to-end tests that start the nest server, connect it to a test database in a local docker container, and test it using the client. Here are some snippets of what I'm attempting to do:
Controller:
#Controller('/api/v1/messages')
export class MessagesController {
constructor(
private messagesService: MessagesService
) {}
#Get()
#UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
private findAll(
#Query() searchCriteria: MessageSearchDto
): Promise<MessageDto[]> {
if (Object.keys(searchCriteria).length > 0)
return this.messagesService.search(searchCriteria);
return this.messagesService.findAll();
}
}
Client:
const http = require('axios');
const dotenv = require('dotenv');
dotenv.config();
export class MessageClient {
public baseUri: string = process.env.MessageClientTarget ?? './';
constructor() {}
public async findAll() {
return await http.get(this.baseTarget());
}
private baseTarget() {
return `${this.baseUri}/api/v1/messages`;
}
}
e2e Test:
describe('MessageController (e2e)', () => {
let app: INestApplication;
let client: MessageClient = new MessageClient();
beforeAll(async () => {
let moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule, MessagesModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('/ (GET)', async done => {
const { data: messages } = await client.findAll()
expect(messages).toEqual([]);
done();
});
});
And .env:
MessageClientTarget=http://localhost:3000
When I attempt to run this, I get the following error: "connect ECONNREFUSED 127.0.0.1:3000"
From what I understand, this is because the createNestApplication method doesn't actually start a server but instead creates a mocked version of the application.
My question is how can I work with INestApplication or TestingModule in order to start the application or what other way do I have to programmatically start a NestJS application. It is important to me that I perform the e2e testing with the Axios based client rather than supertest as a way of testing both the client and the server.
I have verified that the server is supposed to start on port 3000 locally, and I have verified that the client has the correct baseUri set. The address used by the client during testing is: http://localhost:3000/api/v1/messages and was verified by outputting the value to the console during the test. Also, only the database currently lives in a docker container and I have that running correctly. The whole application works perfectly when ran locally and it is only in the test that it is failing.
Please make sure that the test environment where you are running tests is set to node. Add testEnvironemnt:'node' jest parameter in your jest configuration file.
You can run e2e tests with any client of your choice regardless of whether or not you are running a nestjs testing module or the actual nestjs application instance.
I personally use testing module as it makes very easy to mock any third-party dependencies of the application when testing it (that is the main purpose of the testing module, providing an elegant manner to substitute or mock any component that you may want to).

Firestore emulator for testing security rules - running the tests

I have installed the emulator following the instructions at enter link description here and I can start it, so far so good.
After picking some code here and there I have written my first test, here it is:
import * as firebasetesting from '#firebase/testing';
import * as firebase from 'firebase';
import * as fs from 'fs';
const projectId = 'my-firebase-project';
const rules = fs.readFileSync('firestore.rules', 'utf8');
beforeAll(async () => {
// Make your test app load your firestore rules
await firebasetesting.loadFirestoreRules({ projectId, rules });
});
beforeEach(async () => {
// Reset our data from our test database
await firebasetesting.clearFirestoreData({ projectId });
});
after(async () => {
// Shut down all testing Firestore applications after testing is done.
await Promise.all(firebasetesting.apps().map(app => app.delete()));
});
describe("TRACKERS AND ALLIES", () => {
it('TRACKER UP', async () => {
let user = {username: "Bob", uid: 'bobuid'}
let target = { username: "Alice", uid: 'aliceuid'}
const auth = { uid: bob.uid, token: {isadmin: false} };
const app = firebasetesting.initializeTestApp({ projectId, auth }).firestore();
const ref = app.doc('users/'+ user.uid + '/contact/' + target.uid);
await firebasetesting.assertSucceeds(ref.update({ up: true, username: target.uid, timestamp: firebase.firestore.FieldValue.serverTimestamp() }));
});
})
And my question is very simple: how do I run it?
EDIT: I may just add that I am new to Firestore and Javascript in general... The link above simply states
After running a suite of tests, you can access test coverage reports that show how each of your security rules was evaluated.
So I guess it must be simple, but I cannot find the "run" command anywhere...
If you have a nodejs script, run it with node your-script.js. You must have node installed.
If you want to run the script along with the emulator, and shut the emulator down after the script finishes, the page you linked to says:
In many cases you want to start the emulator, run a test suite, and
then shut down the emulator after the tests run. You can do this
easily using the emulators:exec command:
firebase emulators:exec --only firestore "./my-test-script.sh"
If you found the documentation confusing or incomplete, you should use the "send feedback" button at the top right of the page.