.env for jest and nest is not the same - jestjs

I want to test a nest api, for that I create one file .env and for my dev I create a .env.development file.
Which I launch some test with jest my first test uses the good file: .env but the second test use the wrong file .env.development.
How I load the .env in nest :
import {Module} from '#nestjs/common';
import {ConfigService} from './config.service';
#Module({
providers: [{
provide: ConfigService,
useValue: new ConfigService(`.env${process.env.NODE_ENV}`),
}],
exports: [ConfigService],
})
export class ConfigModule {}
How I load the .env in jest :
"test:watch": "NODE_ENV='' jest --watch",
"jest": {
"displayName": {
"name": "API",
"color": "yellow"
},
"verbose": true,
"runner": "jest-serial-runner",
"testSequencer": "../src/test/sequencer.jest.js",
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"preset": "#shelf/jest-mongodb",
"setupFiles": [
"dotenv/config" <------------------------| here
]
},
The test for my .env (is ok the right .env is loaded)
describe('🛠️ tests for env file', () => {
it('should have a db_user', () => {
expect(process.env.db_user).toBeDefined();
});
it('should have a db_pass', () => {
expect(process.env.db_pass).toBeDefined();
});
it('should have a db_uri', () => {
expect(process.env.db_uri).toBeDefined();
});
it('should have a db_port', () => {
expect(process.env.db_port).toBeDefined();
});
it('should have a db_name', () => {
expect(process.env.db_name).toBeDefined();
});
});
The test that loads the wrong .env (I put a wrong ip for the db in .env.development, so the test failed)
import { Test, TestingModule } from '#nestjs/testing';
import * as request from 'supertest';
import {AppModule} from '../app.module';
// remove console.log
console.log = jest.fn();
describe('🗜️ UserController', () => {
let app;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
/** valid test for create a admin user */
it('post /users valid test for create a admin user', () => {
return request(app.getHttpServer())
.post('/users')
.send({
jwt: '',
firstname: 'admin',
lastname: 'admin',
mail: 'admin#gmail.com',
password: 'test'
})
.expect(201);
});
});

It looks like there is a missing . after .env in this line:
new ConfigService(`.env${process.env.NODE_ENV}`)
However, the extra period is only needed when process.env.NODE_ENV contains a value so I would do something like:
const NODE_ENV = process.env.NODE_ENV ? `.${process.env.NODE_ENV}` : '';
//...
new ConfigService(`.env${NODE_ENV}`)
This way you will either end up with new ConfigService('.env') or new ConfigService('.env.development')

Related

NestJS - gRPC Client E2E Test

I am trying to write an e2e test for my NestJS microservice that has a gRPC client. No matter what I do to try and mock the ClientsModule in the e2e test it still seems to not pick up the config and is unable to locate the *.proto file.
Please find a sample of my code below:
The clients module in the app.module.ts
// src/app.module.ts
#Module({
imports: [
HttpModule,
...
...
ClientsModule.register([
{
name: 'USERS_PACKAGE',
transport: Transport.GRPC,
options: {
package: 'users',
credentials: credentials.createInsecure(),
protoPath: join(__dirname, '../proto/users.proto'),
url: 'users-grpc-server.users:50051',
},
},
]),
],
controllers: [UsersController],
providers: [UsersService, UsersClient],
})
export class AppModule {}
The users client to make the gRPC call to the gRPC Server NestJS microservice:
// src/clients/users.client.ts
#Injectable()
export class UsersClient implements OnModuleInit {
private readonly logger: Logger = new Logger(UsersClient.name);
private readonly API_KEY: string = this.configService.get<string>('services.v1.api-key');
private usersController: usersController;
constructor(#Inject('USERS_PACKAGE') private client: ClientGrpc, private configService: ConfigService) {}
onModuleInit() {
this.usersController = this.client.getService<UsersController>('UsersController');
}
async getUsers(headers: IncomingHttpHeaders, userId: string): Promise<Users> {
let users: Users = null;
const metadata = new Metadata();
metadata.add('Authorization', headers.authorization);
metadata.add('x-api-key', this.API_KEY);
try {
users = await this.usersController.getUsers({}, metadata);
} catch (e) {
const { message, response: { status = 500 } = {}, stack } = e;
this.logger.error(stack);
throw new HttpException(message, status);
}
return users;
}
}
The update nest-cli.json
{
"collection": "#nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"assets": [{"include": "../config/*.yaml", "outDir": "./dist/config"}, {"include": "**/*.proto", "outDir": "./dist"}]
}
}
The e2e test configuration
// test/app.e2e-spec.json
let app: NestFastifyApplication;
let httpService: HttpService;
let cacheManager: Cache;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
HttpModule,
...
...
ClientsModule,
],
})
.overrideProvider(ClientsModule)
.useValue({
name: 'USERS_PACKAGE',
transport: Transport.GRPC,
options: {
package: 'users',
credentials: ServerCredentials.createInsecure(),
protoPath: join(__dirname, '../src/proto/users.proto'),
url: 'localhost:50051',
},
})
.compile();
app = module.createNestApplication(new FastifyAdapter());
await app.init();
await app.getHttpAdapter().getInstance().ready();
httpService = module.get<HttpService>(HttpService);
cacheManager = module.get<Cache>(CACHE_MANAGER);
});
The error:
● Test suite failed to run
The invalid .proto definition (file at "/users-service-grpc-client/app/proto/users.proto" not found)
> 26 | ClientsModule.register([
| ^
27 | {
28 | name: 'USERS_PACKAGE',
29 | transport: Transport.GRPC,
at ClientGrpcProxy.loadProto (node_modules/#nestjs/microservices/client/client-grpc.js:220:39)
at ClientGrpcProxy.createClients (node_modules/#nestjs/microservices/client/client-grpc.js:193:34)
at new ClientGrpcProxy (node_modules/#nestjs/microservices/client/client-grpc.js:29:33)
at Function.create (node_modules/#nestjs/microservices/client/client-proxy-factory.js:27:24)
at node_modules/#nestjs/microservices/module/clients.module.js:12:80
at Array.map (<anonymous>)
at Function.register (node_modules/#nestjs/microservices/module/clients.module.js:10:41)
at Object.<anonymous> (src/app.module.ts:26:19)
It seems like the the e2e test isnt using the ClientsModule configuration from the test and is still using the ClientsModule configuration from the app.module.ts.
Does anyone know of a way to configure this correctly ?
I am not sure how this is working but I changed the ClientsModule in the src/app.module.ts to be registerAsync because I wanted to make the url dynamic and it now seems to be working correctly
// src/app.module.ts
#Module({
imports: [
HttpModule,
...
...
ClientsModule.registerAsync([
{
name: 'USERS_PACKAGE',
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
transport: Transport.GRPC,
options: {
package: 'users',
credentials: credentials.createInsecure(),
protoPath: join(__dirname, '../proto/users.proto'),
url: configService.get<string>('services.users-grpc-server-service.url'),
},
}),
inject: [ConfigService],
},
]),
],
controllers: [UsersController],
providers: [UsersService, UsersClient],
})
export class AppModule {}
Note: In the E2E test, I also had to mock the grpc call using overrideProvider
// test/app.e2e-spec.ts
const module: TestingModule = await Test.createTestingModule({
imports: [
HttpModule,
...
...
ClientsModule,
],
})
.overrideProvider('USERS_PACKAGE')
.useValue({
getService: () => ({
getUsers: jest.fn().mockReturnValue({
"name": "test user",
"age": "55",
"height": "1.89"
}),
}),
})
.compile();

Jest test runs indefinitely, & yarn run test --runInBand throws error Command failed with signal "SIGSEGV"

My test runs indefinitely without results (see image below). I am pretty new to setting up backend testing environment. Any guidance is much appreciated.
I have provided the snippets of my code, please let me know if you need any additional information.
I tried the below commands.
--clearCache option which did not work
--runInBand option lead to a new error, error Command failed with signal "SIGSEGV".
// testHelper.js
import { DataSource, DataSourceOptions } from "typeorm";
import Database from "better-sqlite3";
export class TestHelper {
private static _instance: TestHelper;
private constructor() { }
public static get instance(): TestHelper {
if (!this._instance) this._instance = new TestHelper();
return this._instance;
}
private dbConnect!: DataSource;
private testdb!: any;
async setupTestDB() {
this.testdb = new Database(":memory:", { verbose: console.log });
this.dbConnect = new DataSource({
name: "default",
type: "better-sqlite3",
database: ":memory:",
entities: ["src/entities/**/*.ts"],
synchronize: true,
} as DataSourceOptions);
await this.dbConnect.initialize()
}
teardownTestDB() {
this.dbConnect.destroy();
this.testdb.close();
}
}
// test.ts
import MyEntity from "entities/MyEntity";
import { TestHelper } from './__utils/testHelper';
beforeAll(async () => {
return await TestHelper.instance.setupTestDB();
});
afterAll(() => {
return TestHelper.instance.teardownTestDB();
});
test("create test data and fetch it", async () => {
await MyEntity.insert({
name: "sqlTestEntity"
});
let sqlTestEntity = await MyEntity.find({
where: {
id: '1'
}
});
expect(sqlTestEntity[0].name).toBe("sqlTestEntity");
});
//package.json
"scripts": {
....
"test": "NODE_OPTIONS=--experimental-vm-modules npx jest",
....
}
"typeorm": "0.3",
"#types/jest": "^28.1.7",
"jest": "^28.1.3",
// jest.config.js
/** #type {import('ts-jest/dist/types').InitialOptionsTsJest} */
export default {
preset: 'ts-jest',
testEnvironment: 'node',
testTimeout: 20000,
roots: ['src'],
modulePaths: ['<rootDir>/src'],
testPathIgnorePatterns: [
'<rootDir>/node_modules/', "/__utils"
],
moduleDirectories: [
"src"
],
transform: {
"^.+\\.js?$": "babel-jest",
"^.+\\.ts?$": "ts-jest"
},
testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(js?|tsx?)$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
testEnvironment: "node",
"moduleNameMapper": {
"src/(.*)": "<rootDir>/src/$1"
},
globals: {
"ts-jest": {
"useESM": true
}
},
extensionsToTreatAsEsm: ['.ts'],
moduleNameMapper: {
'^(\\.{1,2}/.*)\\.js$': '$1',
},
};

Mocha: Nested describe() structure fails to register tests when using firebase rules testing library (SDK 9)

I have a Typescript Mocha setup that I'm using to test my Firestore rules. It uses the latest version of the #firebase/rules-unit-testing library that is part of the Version 9 Firebase SDK.
It also uses the latest Firestore client library that has the newer API that is tree-shakable.
My problem is that Mocha doesn't seem to be able to recognise tests in my nested describe() structure AFTER I call the initializeTestEnvironment() promise.
If you see the output you will see only two tests get executed.
package.json
{
"name": "blocks",
"version": "1.0.0",
"description": "A new Flutter project.",
"main": "index.js",
"private": true,
"scripts": {
"setup": "firebase setup:emulators:firestore",
"test": "firebase emulators:exec --only firestore \"mocha firestore.rules.spec.ts\""
},
"devDependencies": {
"#firebase/rules-unit-testing": "^2.0.2",
"#types/mocha": "^8.2.2",
"firebase-tools": "^10.7.1",
"chai": "^4.3.4",
"mocha": "^9.2.2",
"ts-node": "^9.1.1",
"typescript": "^4.2.4"
},
"mocha": {
"recursive": true,
"reporter": "nyan",
"require": "ts-node/register",
"watch-extensions": "ts"
},
"prettier": {
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true
}
}
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true,
"importHelpers": true,
"module": "commonjs",
"target": "es2019",
"types": [
"node"
]
}
}
firestore.rules.spec.ts
import { before, beforeEach, after } from 'mocha'
import { readFileSync } from 'fs'
import {
assertFails,
assertSucceeds,
initializeTestEnvironment,
} from '#firebase/rules-unit-testing'
import { doc, setDoc, getDoc, updateDoc, deleteDoc } from 'firebase/firestore'
const PROJECT_ID = 'blocks-firestore-rules-testing'
describe('A test that should get run', async () => {
it('should pass', async () => {
return true
})
})
describe('Firestore rules', async () => {
it('should pass also', async () => {
return true
})
const testEnv = await initializeTestEnvironment({
projectId: PROJECT_ID,
firestore: {
rules: readFileSync('firestore.rules', 'utf-8'),
},
})
beforeEach(async () => {
testEnv.cleanup()
await testEnv.clearFirestore()
})
after(async () => {
testEnv.cleanup()
await testEnv.clearFirestore()
})
const uid = 'normal-user'
const user = testEnv.authenticatedContext(uid)
const userRef = doc(user.firestore(), `users/${uid}/`)
const dummyUserDoc = { displayName: 'John Smith' }
it('ensures can create their own user document', async () => {
await assertSucceeds(setDoc(userRef, dummyUserDoc))
})
it('ensures they can read their user document', async () => {
testEnv.withSecurityRulesDisabled(async () => {
setDoc(userRef, dummyUserDoc)
})
await assertSucceeds(getDoc(userRef))
})
it('ensures they can update their user document', async () => {
testEnv.withSecurityRulesDisabled(async () => {
setDoc(userRef, dummyUserDoc)
})
await assertSucceeds(updateDoc(userRef, { foo: 'baz' }))
})
it('ensures cannot delete their user document', async () => {
testEnv.withSecurityRulesDisabled(async () => {
setDoc(userRef, dummyUserDoc)
})
await assertFails(deleteDoc(userRef))
})
})
Command line output:
➜ blocks git:(main) ✗ npm test
> blocks#1.0.0 test
> firebase emulators:exec --only firestore "mocha firestore.rules.spec.ts"
i emulators: Starting emulators: firestore
i firestore: Firestore Emulator logging to firestore-debug.log
i Running script: mocha firestore.rules.spec.ts
2 -_-_,------,
0 -_-_| /\_/\
0 -_-^|__( ^ .^)
-_- "" ""
2 passing (2ms)
✔ Script exited successfully (code 0)
i emulators: Shutting down emulators.
i firestore: Stopping Firestore Emulator
i hub: Stopping emulator hub

How to do unit testing to #Inject with ClientKafka in NestJS

I need to do some unit test to a kafka implementation in my project with NestJS but I don't know how to do it.
I have a Service thats inject a Client Kafka
export class Service {
private static readonly logger = new Logger(ProducerService.name);
constructor(
#Inject('kafka-registrar') private client: ClientKafka,
private someOtherService: SomeOtherService,
) {}
Module
#Module({
imports: [
ClientsModule.register([
{
name: 'kafka-registrar',
transport: Transport.KAFKA,
options: {
client: {
clientId: 'hero',
brokers: ['localhost:9092'],
},
consumer: {
groupId: '1',
},
},
},
]),
SomeOtherService,
],
providers: [Service],
})
export class Module {}
Unit test
describe('Test Controller', () => {
let clientKafka: ClientKafka;
let someOtherService: SomeOtherService;
let producerService: ProducerService;
beforeEach(async () => {
const moduleRef = await Test.createTestingModule({
providers: [
ProducerService,
{
provide: SchemaRegistryService,
useValue: {
encodeWithId: jest.fn(),
},
},
{
provide: ClientKafka,
useValue: {
emit: jest.fn(),
},
},
],
}).compile()
clientKafka = moduleRef.get(ClientKafka);
schemaRegistryService = moduleRef.get(SchemaRegistryService);
producerService = moduleRef.get(ProducerService);
});
The project give me this error:
Error: Nest can't resolve dependencies of the ProducerService (?, SchemaRegistryService). Please make sure that the argument kafka-registrar at index [0] is available in the RootTestModule context.
Potential solutions:
- If kafka-registrar is a provider, is it part of the current RootTestModule?
- If kafka-registrar is exported from a separate #Module, is that module imported within RootTestModule?
#Module({
imports: [ /* the Module containing kafka-registrar */ ]
})
I don't know how to resolve this in NestJS. For example in Java,I belive that this can be with #Mock ClientKafka clientKafka bit I dont have any other experience with NestJS... Please helpme! :)
In your test file, you can change provide: ClientKafka to this provide: 'kafka-registrar'.
const moduleRef = await Test.createTestingModule({
providers: [
ProducerService,
{
provide: SchemaRegistryService,
useValue: {
encodeWithId: jest.fn(),
},
},
{
provide: 'kafka-registrar',
useValue: {
emit: jest.fn(),
},
},
],
}).compile()

global is not defined in jest test file

global variable in JS is not able to access in jest test files, also not able to change the values which are already defined in global.
the work around I have did is as follow:
require('./../../../../bin/global');
describe('gloablAccessMethod', () => {
beforeEach(() => {
// jest.spyOn(regionConfig,'global.regionConfig').mockImplementation(() => ({
// 'ENABLE_CONFIG' : false
// }));
global.regionConfig.ENABLE_CONFIG = false;
});
const PHONE_PREFIX_SG = 65;
it('should access mock global variables', () =>{
//some sample tests
});
});
jest configuration
"jest": {
"timers": "fake",
"testEnvironment": "node",
"setupFiles": [
"./.jest/setEnv.js"
],
"setupFilesAfterEnv": [
"./.jest/setup.js"
],
"coverageReporters": [
"text",
"cobertura"
],
"collectCoverage": true,
"collectCoverageFrom": [
"src/**/*.{js,jsx}"
]
},```

Resources