使用Nestjs测试控制器时嘲笑NewRelic模块
我刚刚在Nestjs应用程序上安装了NewRelic,它工作正常。但是现在执行测试时正在抛出异常:
console.error 错误:新遗物要求您命名此应用程序! 在您的newRelic.js文件或设置环境变量中设置app_name new_relic_app_name。 没有启动!
在测试下,NewRelic模块在/src/newrelic.ts下找不到配置时,似乎
。新遗物本身正在应用程序中工作。它只是在打破测试。
这是 /src下的我的newRelic.ts文件:
'use strict'
/**
* New Relic agent configuration.
*
* See lib/config/default.js in the agent distribution for a more complete
* description of configuration variables and their potential values.
*/
exports.config = {
/**
* Array of application names.
*/
app_name: ['product-metrics-dev'],
/**
* Your New Relic license key.
*/
license_key: 'redacted',
logging: {
/**
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
* issues with the agent, 'info' and higher will impose the least overhead on
* production applications.
*/
level: 'info'
},
/**
* When true, all request headers except for those listed in attributes.exclude
* will be captured for all traces, unless otherwise specified in a destination's
* attributes include/exclude lists.
*/
allow_all_headers: true,
application_logging: {
forwarding: {
/**
* Toggles whether the agent gathers log records for sending to New Relic.
*/
enabled: true
}
},
attributes: {
/**
* Prefix of attributes to exclude from all destinations. Allows * as wildcard
* at end.
*
* NOTE: If excluding headers, they must be in camelCase form to be filtered.
*
* @env NEW_RELIC_ATTRIBUTES_EXCLUDE
*/
exclude: [
'request.headers.cookie',
'request.headers.authorization',
'request.headers.proxyAuthorization',
'request.headers.setCookie*',
'request.headers.x*',
'response.headers.cookie',
'response.headers.authorization',
'response.headers.proxyAuthorization',
'response.headers.setCookie*',
'response.headers.x*'
]
}
}
这是一个简单的服务,正在运行一些新代码:
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Account } from './entities/account.entity';
import { v4 as uuidv4 } from 'uuid';
import { ObjectID } from 'bson';
import * as newrelic from 'newrelic';
@Injectable()
export class AccountsService {
private readonly logger = new Logger('Accounts Service');
constructor(@InjectModel(Account.name) private readonly accountModel: Model<Account>) {}
async createAccount(name): Promise<Account> {
//checks if name already exists
const acc: Account = await this.accountModel.findOne({ name });
if (acc) {
throw new HttpException('Account name already exists', HttpStatus.BAD_REQUEST);
}
const now = new Date();
const id = new ObjectID();
const account = await this.accountModel.create({ _id: id, name, createdAt: now });
newrelic.recordMetric('accounts_created', 1);
newrelic.recordCustomEvent('accountCreated', { accountId: id, groupName: account.name });
return account;
}
async getById(accountId: string): Promise<Account> {
return await this.accountModel.findOne({ _id: accountId });
}
}
和调用它的控制器:
import { Body, Controller, Logger, Post } from '@nestjs/common';
import { AccountsService } from './accounts.service';
import { CreateAccountData } from './dto/create-account-data';
@Controller('accounts')
export class AccountsController {
constructor(private accountsService: AccountsService) {}
private readonly logger = new Logger('Accounts Controller');
@Post('/')
async createAccount(
@Body() body: CreateAccountData,
) {
this.logger.debug('Create account request received')
this.logger.debug(`Name: ${body.name}`)
return await this.accountsService.createAccount(body.name);
}
}
以及抛出错误的控制器的测试:
import { getModelToken, MongooseModule } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Account, AccountSchema } from '../entities/account.entity';
import { AccountsController } from '../accounts.controller';
import { AccountsService } from '../accounts.service';
import { CreateAccountData } from '../dto/create-account-data';
import newrelic from 'newrelic';
describe('AccountsController', () => {
let controller: AccountsController;
let service: AccountsService;
const now = new Date();
const fakeAccount: Account = {
_id: 'lalal',
name: 'Palmeiras',
createdAt: now,
};
const postBody: CreateAccountData = {
name: 'Palmeiras',
};
class accountModel {
constructor(private data) {}
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue([fakeAccount]);
static findOne = jest.fn().mockResolvedValue(fakeAccount);
static create = jest.fn().mockResolvedValue(fakeAccount);
static findOneAndUpdate = jest.fn().mockResolvedValue(fakeAccount);
static deleteOne = jest.fn().mockResolvedValue(true);
}
jest.mock('newrelic', () => {
return {
recordMetric: jest.fn(),
recordCustomEvent: jest.fn(),
};
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AccountsController],
providers: [
AccountsService,
{
provide: getModelToken(Account.name),
useValue: accountModel,
},
],
}).compile();
controller = module.get<AccountsController>(AccountsController);
service = module.get<AccountsService>(AccountsService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('create account POST /', () => {
it('should call the right service', async () => {
const method = jest.spyOn(service, 'createAccount').mockReturnValue(Promise.resolve(fakeAccount));
const nr = jest.spyOn(newrelic, 'recordCustomEvent').mockReturnValue(null);
const u = await controller.createAccount(postBody);
expect(newrelic.recordCustomEvent).toHaveBeenCalledWith('hello', 1);
expect(method).toHaveBeenCalledWith(postBody.name);
expect(u).toEqual(fakeAccount);
});
});
});
我的理解是,通过使用代码,下面,我会嘲笑Newrelic服务:
jest.mock('newrelic', () => {
return {
recordMetric: jest.fn(),
recordCustomEvent: jest.fn(),
};
});
我的问题是:
- 嘲笑新进口的正确方法是什么?
- 为什么NewRelic Import不找到其配置文件?
I just installed newrelic on my NestJS app and it is working fine. But now it's throwing exceptions while executing tests:
console.error
Error: New Relic requires that you name this application!
Set app_name in your newrelic.js file or set environment variable
NEW_RELIC_APP_NAME. Not starting!
Seems like, while under the test, the newrelic module cannot find the configuration located under /src/newrelic.ts.
New Relic itself is working in the app. It's just breaking the tests.
Here is my newrelic.ts file under /src:
'use strict'
/**
* New Relic agent configuration.
*
* See lib/config/default.js in the agent distribution for a more complete
* description of configuration variables and their potential values.
*/
exports.config = {
/**
* Array of application names.
*/
app_name: ['product-metrics-dev'],
/**
* Your New Relic license key.
*/
license_key: 'redacted',
logging: {
/**
* Level at which to log. 'trace' is most useful to New Relic when diagnosing
* issues with the agent, 'info' and higher will impose the least overhead on
* production applications.
*/
level: 'info'
},
/**
* When true, all request headers except for those listed in attributes.exclude
* will be captured for all traces, unless otherwise specified in a destination's
* attributes include/exclude lists.
*/
allow_all_headers: true,
application_logging: {
forwarding: {
/**
* Toggles whether the agent gathers log records for sending to New Relic.
*/
enabled: true
}
},
attributes: {
/**
* Prefix of attributes to exclude from all destinations. Allows * as wildcard
* at end.
*
* NOTE: If excluding headers, they must be in camelCase form to be filtered.
*
* @env NEW_RELIC_ATTRIBUTES_EXCLUDE
*/
exclude: [
'request.headers.cookie',
'request.headers.authorization',
'request.headers.proxyAuthorization',
'request.headers.setCookie*',
'request.headers.x*',
'response.headers.cookie',
'response.headers.authorization',
'response.headers.proxyAuthorization',
'response.headers.setCookie*',
'response.headers.x*'
]
}
}
Here is a simple service that's running some NewRelic code:
import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { Account } from './entities/account.entity';
import { v4 as uuidv4 } from 'uuid';
import { ObjectID } from 'bson';
import * as newrelic from 'newrelic';
@Injectable()
export class AccountsService {
private readonly logger = new Logger('Accounts Service');
constructor(@InjectModel(Account.name) private readonly accountModel: Model<Account>) {}
async createAccount(name): Promise<Account> {
//checks if name already exists
const acc: Account = await this.accountModel.findOne({ name });
if (acc) {
throw new HttpException('Account name already exists', HttpStatus.BAD_REQUEST);
}
const now = new Date();
const id = new ObjectID();
const account = await this.accountModel.create({ _id: id, name, createdAt: now });
newrelic.recordMetric('accounts_created', 1);
newrelic.recordCustomEvent('accountCreated', { accountId: id, groupName: account.name });
return account;
}
async getById(accountId: string): Promise<Account> {
return await this.accountModel.findOne({ _id: accountId });
}
}
And the controller that calls it:
import { Body, Controller, Logger, Post } from '@nestjs/common';
import { AccountsService } from './accounts.service';
import { CreateAccountData } from './dto/create-account-data';
@Controller('accounts')
export class AccountsController {
constructor(private accountsService: AccountsService) {}
private readonly logger = new Logger('Accounts Controller');
@Post('/')
async createAccount(
@Body() body: CreateAccountData,
) {
this.logger.debug('Create account request received')
this.logger.debug(`Name: ${body.name}`)
return await this.accountsService.createAccount(body.name);
}
}
And the test for the controller that is throwing the error:
import { getModelToken, MongooseModule } from '@nestjs/mongoose';
import { Test, TestingModule } from '@nestjs/testing';
import { Account, AccountSchema } from '../entities/account.entity';
import { AccountsController } from '../accounts.controller';
import { AccountsService } from '../accounts.service';
import { CreateAccountData } from '../dto/create-account-data';
import newrelic from 'newrelic';
describe('AccountsController', () => {
let controller: AccountsController;
let service: AccountsService;
const now = new Date();
const fakeAccount: Account = {
_id: 'lalal',
name: 'Palmeiras',
createdAt: now,
};
const postBody: CreateAccountData = {
name: 'Palmeiras',
};
class accountModel {
constructor(private data) {}
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue([fakeAccount]);
static findOne = jest.fn().mockResolvedValue(fakeAccount);
static create = jest.fn().mockResolvedValue(fakeAccount);
static findOneAndUpdate = jest.fn().mockResolvedValue(fakeAccount);
static deleteOne = jest.fn().mockResolvedValue(true);
}
jest.mock('newrelic', () => {
return {
recordMetric: jest.fn(),
recordCustomEvent: jest.fn(),
};
});
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AccountsController],
providers: [
AccountsService,
{
provide: getModelToken(Account.name),
useValue: accountModel,
},
],
}).compile();
controller = module.get<AccountsController>(AccountsController);
service = module.get<AccountsService>(AccountsService);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
describe('create account POST /', () => {
it('should call the right service', async () => {
const method = jest.spyOn(service, 'createAccount').mockReturnValue(Promise.resolve(fakeAccount));
const nr = jest.spyOn(newrelic, 'recordCustomEvent').mockReturnValue(null);
const u = await controller.createAccount(postBody);
expect(newrelic.recordCustomEvent).toHaveBeenCalledWith('hello', 1);
expect(method).toHaveBeenCalledWith(postBody.name);
expect(u).toEqual(fakeAccount);
});
});
});
My understanding was that, by using the code below, I'd be mocking the newrelic service:
jest.mock('newrelic', () => {
return {
recordMetric: jest.fn(),
recordCustomEvent: jest.fn(),
};
});
My questions are:
- What's the right way to mock the newrelic import?
- Why isn't the newrelic import finding its config file?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在新的遗物优先层次结构中,环境变量覆盖了配置文件中的内容。当我尝试此操作时,我必须从数组字符串外部更改应用程序名称,并且可以更改。
以下内容:
因此,您还可以完全删除应用程序字符串,而不是
让环境变量 new_relic_app_name 确定应用程序的名称,与过程配对时,这可能特别有用。 Env对于单独的资源进行单个构建人工制品之类的分期和生产工作流程。
In the new relic precedence hierarchy, environment variables overwrite what is in your config file. When I tried this I had to change the app name from outside of the array string and it worked.
So instead of:
it would be
For what its worth, you can also remove the app string entirely and let the environment variable NEW_RELIC_APP_NAME decide the name of your application, this can be particularly useful when paired with process.env for things like staging and production workflows with separate resources for a single build artefact.
我能够通过开玩笑的配置将新的遗物配置变量作为环境变量进行解决问题。
I was able to work-around the problem by passing the new relic configuration variables as environment variables through jest configuration.