关于 nebula-logger-testing-your-code
nebula-logger-testing-your-code 是面向在 Apex 或 LWC 代码中使用 Nebula Logger 的开发者的测试技能。它涵盖检查缓冲条目、验证已保存记录、控制测试中的保存行为,以及在 LWC 测试中模拟日志调用。
主要功能
- 01断言缓冲日志数量
- 02验证已保存的日志记录
- 03控制保存行为并清空缓冲区
- 04处理异步测试中的事件传递
- 05模拟并断言 LWC 日志调用
适用场景
- 01测试会写入日志的 Apex 代码
- 02检查已保存的消息和场景
- 03在测试期间阻止日志持久化
- 04验证 LWC 测试中的日志参数
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jongpie/ nebulalogger --skill nebula-logger-testing-your-code安装后在 Claude Code 中输入/nebula-logger-testing-your-code确认已加载。
详细介绍
测试使用 Nebula Logger 的代码
本技能适用于自己的 Apex 或 LWC 代码调用 Logger.info(...)、Logger.error(...) 等,并希望通过测试确认记录了正确内容的开发者。这里只使用 Nebula Logger 的 global API,因此无论组织安装的是解锁包还是托管包,都可以使用相同的技巧。
缓冲区模型
Nebula Logger 会在事务期间将日志条目缓冲在内存中。Logger.saveLog() 会将缓冲的条目移至保存流程(平台事件、队列任务、REST 或同步 DML)——有关保存方法的选择,请参阅 nebula-logger-instrumentation。
对于测试,缓冲区就是观察点:你可以添加条目,在调用 saveLog() 之前检查缓冲区大小,然后调用 saveLog() 并查询生成的记录,或者调用 flushBuffer() 丢弃这些条目而不持久化。这三个 API 都是 global 的。
在保存前断言缓冲区内容
Logger.getBufferSize() 会返回当前缓冲的条目数。使用它可以确认代码添加了预期的条目,而无需持久化这些条目。
@IsTest
static void it_should_log_a_warning_when_credit_check_fails() {
Account account = new Account(Name = 'Test Account');
insert account;
System.Test.startTest();
new CreditCheckService().evaluate(account.Id);
System.Test.stopTest();
System.Assert.areEqual(1, Logger.getBufferSize(), 'Expected exactly one entry buffered');
}
由于 getBufferSize() 在触发 saveLog() 之前运行,因此无需等待平台事件发布,断言也能通过。
保存后断言持久化记录
如果需要断言消息文本、日志级别、关联记录或场景,请保存日志并查询持久化的记录。日志管理端的全部自定义对象如下:
Log__c- 每个日志记录事务对应一条记录。LogEntry__c- 每次调用Logger.info(...)/.error(...)等都会对应一条记录。LogEntryTag__c- 应用标签时,LogEntry__c与LoggerTag__c之间的关联对象。LoggerScenario__c- 记录各事务中使用的每个唯一Logger.setScenario(...)值。LoggerTag__c- 由LogEntryTag__c引用的标签目录。
使用默认的 EVENT_BUS 保存方法时,平台事件会在 System.Test.stopTest() 期间触发,并填充上述所有对象。
何时还需要 System.Test.getEventBus().deliver()
System.Test.stopTest() 只会传递在 stopTest 边界之前排队的平台事件。任何在 stopTest 自身刷新异步工作期间发布的 LogEntryEvent__e 记录,都需要额外调用 System.Test.getEventBus().deliver(),之后 LogEntry__c / Log__c 记录才会生成。以下两种情况需要注意:
Logger.setSaveMethod(Logger.SaveMethod.QUEUEABLE)或Logger.saveLog(Logger.SaveMethod.QUEUEABLE)- Nebula Logger 基于队列任务的保存会在队列任务内部发布平台事件。队列任务会在stopTest期间运行,但它发布的事件需要再进行一次传递。- 被测异步代码本身会记录日志 - 在
execute中调用Logger.info(...)/.error(...)/saveLog()的System.Queueable、System.Finalizer或Database.Batchable会在异步上下文中发布平台事件。stopTest会运行异步作业,但不会传递该作业产生的平台事件。
@IsTest
static void it_should_log_the_error_from_a_failed_queueable() {
System.Test.startTest();
System.enqueueJob(new ExampleFailedQueueable());
System.Test.stopTest();
System.Test.getEventBus().deliver();
List<LogEntry__c> entries = [SELECT LoggingLevel__c FROM LogEntry__c WHERE LoggingLevel__c = 'ERROR'];
System.Assert.areEqual(1, entries.size());
}
对于使用默认 EVENT_BUS 保存方法的同步代码,只调用 stopTest 就够了,无需额外调用 deliver。
@IsTest
static void it_should_log_the_error_message_when_credit_check_throws() {
Account account = new Account(Name = 'Test Account');
insert account;
System.Test.startTest();
System.Exception thrownException;
try {
new CreditCheckService().evaluate(account.Id);
System.Assert.fail('Expected an exception');
} catch (System.Exception ex) {
thrownException = ex;
}
System.Test.stopTest();
System.Assert.areNotEqual('Expected an exception', thrownException.getMessage(), 'The wrong exception was thrown!');
List<LogEntry__c> entries = [
SELECT LoggingLevel__c, Message__c, RecordId__c, ExceptionMessage__c, ExceptionType__c
FROM LogEntry__c
];
System.Assert.areEqual(1, entries.size());
System.Assert.areEqual('ERROR', entries[0].LoggingLevel__c);
System.Assert.areEqual(thrownException.getMessage(), entries[0].ExceptionMessage__c);
System.Assert.areEqual(thrownException.getTypeName(), entries[0].ExceptionType__c);
}
控制是否持久化条目
默认情况下,Nebula Logger 会在测试上下文中遵循 LoggerSettings__c:如果组织默认设置启用了日志记录,且级别适当,条目就会持久化。如果你希望测试运行被测代码而不触及数据库:
- 在测试开头调用
Logger.suspendSaving()。后续的saveLog()调用都不会执行任何操作——不会插入记录、不会发布平台事件,也不会更改缓冲区。 - 调用
Logger.resumeSaving()可恢复保存。保存暂停期间累积在缓冲区中的所有条目仍会保留,因此下一次调用saveLog()时会将它们全部刷新。如果你想丢弃这些条目而不是持久化它们,请在恢复保存前调用Logger.flushBuffer()。 - 如果你只想避免测试触发的条目在设置完成前累积,请在测试开头调用
Logger.flushBuffer()。 - 编写共享测试工具时,可调用
Logger.isSavingSuspended()来避免测试污染。
@IsTest
static void it_should_not_persist_log_records_when_saving_is_suspended() {
Logger.suspendSaving();
new CreditCheckService().evaluate(fakeAccountId());
Logger.saveLog();
System.Assert.isTrue([SELECT COUNT() FROM Log__c] == 0);
}
Nebula Logger 的 CMDT 记录在测试中也会生效
Salesforce 在测试查询 CMDT 时会返回组织中的实际自定义元数据记录——平台不会像回滚 sObject 数据那样回滚这些记录。Nebula Logger 利用了这一点:内部选择器层会读取已部署到组织中的 LoggerParameter__mdt、LogEntryDataMaskRule__mdt、LogEntryTagRule__mdt、LoggerFieldMapping__mdt、LoggerPlugin__mdt、LoggerScenarioRule__mdt、LoggerSObjectHandler__mdt 和 LogStatus__mdt 记录,并在测试中按照运行时的方式使用它们。
对订阅方自己的测试而言,实际影响如下:
- 更改组织中的
LoggerParameter__mdt.Value__c(例如,将QueryUserDataSynchronously从true改为false,或为预期为布尔值的参数设置非布尔值)会影响测试行为,而不只是运行时行为。依赖开箱即用值的测试可能会在配置更改后开始失败。 - 部署的新
LogEntryDataMaskRule__mdt记录也会在测试中屏蔽敏感数据,因此对原始消息文本的断言需要预期经过屏蔽的文本。 - 新的
LoggerPlugin__mdt记录也会在测试中执行。如果插件有副作用(调用外部接口、DML、平台事件),测试就需要对这些行为进行模拟或加以考虑。 LoggerScenario__mdt记录在测试时会像运行时一样覆盖LoggerSettings__c。
应将 CMDT 更改视为行为更改,并在部署对 Nebula Logger CMDT 记录的任何自定义配置后重新运行相关测试套件。Nebula Logger 内部的 LoggerConfigurationSelector.useMocks() 接口并非 global,因此订阅方测试无法使用它——受支持的做法是让测试知晓组织的 CMDT 状态,并在测试需要跳过持久化相关操作时使用 Logger.suspendSaving() / flushBuffer()。
LWC 测试注意事项
对于使用 sfdx-lwc-jest 的 LWC 测试,请模拟 c/logger 模块,并断言组件调用了正确的条目级方法,且传入了正确的消息 / 记录。
// __tests__/paymentPanel.test.js
import { createElement } from 'lwc';
import PaymentPanel from 'c/paymentPanel';
const mockLogger = {
setScenario: jest.fn(),
info: jest.fn().mockReturnThis(),
error: jest.fn().mockReturnThis(),
setExceptionDetails: jest.fn().mockReturnThis(),
saveLog: jest.fn()
};
jest.mock('c/logger', () => ({
getLogger: () => mockLogger
}));
describe('c-payment-panel', () => {
it('logs an INFO entry when the panel initializes', () => {
const element = createElement('c-payment-panel', { is: PaymentPanel });
document.body.appendChild(element);
expect(mockLogger.setScenario).toHaveBeenCalledWith('Payment UI');
expect(mockLogger.info).toHaveBeenCalledWith('Payment panel initialized');
expect(mockLogger.saveLog).toHaveBeenCalled();
});
});
按照仓库的测试约定,应断言传递给日志记录器方法的参数(toHaveBeenCalledWith(...)),而不只是断言这些方法曾被调用。仅使用 toHaveBeenCalled() 可能会让消息文本丢失或乱码之类的回归问题漏网。
适用于测试代码的全局 API
只有 global API 支持在你自己的测试代码中使用。任何 public 内容都属于 Nebula Logger 的内部接口,可能会在不另行通知的情况下发生变化——请参阅 nebula-logger-instrumentation 中的“受支持的 API 接口”部分。
以下 API 均为 global,可在任一软件包中安全地从订阅者的测试套件调用:
Logger.getBufferSize()- 查看缓冲的条目数量。Logger.suspendSaving()/Logger.resumeSaving()/Logger.isSavingSuspended()- 阻止或控制持久化。Logger.flushBuffer()- 丢弃缓冲的条目,不进行持久化。Logger.getTransactionId()- 在断言持久化记录时关联测试日志。Logger.setScenario(...)/Logger.endScenario(...)- 设置测试专用的场景,便于识别持久化日志。- 特定级别的方法(
Logger.info(...)、Logger.error(...)等)以及Logger.saveLog()。
相关技能
- nebula-logger-instrumentation - 正在测试的 API。
- nebula-logger-best-practices - 被测代码应遵循的约定。