我有一些代码,在构造函数中创建 PerformanceObserver,我试图找出如何模拟它,以便我可以断言
observe
正在观察者上被调用,并且还模拟正在生成的性能条目,这将导致回调被调用,并断言回调实际上被调用。构造函数中的代码如下所示:
const { _tracking } = props;
this.resourceObserver = new PerformanceObserver((list) => {
const resourceEntries = list.getEntriesByType(
'resource',
) as PerformanceResourceTiming[];
for (const resource of resourceEntries) {
resourceObserverCallback(_tracking, resource);
}
performance.clearResourceTimings();
});
this.resourceObserver.observe({ entryTypes: ['resource'] });
有人有什么建议吗?
编辑:
我可以断言,通过创建一个
observe
函数来在观察者上调用 createResourceObserver
,该函数返回传入的模拟或真实的 PerformanceObserver
,这解决了部分问题,但我仍然不确定如何在测试中锻炼观察者。
该函数如下所示:
createResourceObserver() {
const { _mockObserver, _tracking } = this.props;
if (_mockObserver) {
return _mockObserver;
}
const observer = new PerformanceObserver((list) => {
const resourceEntries = list.getEntriesByType(
'resource',
) as PerformanceResourceTiming[];
for (const resource of resourceEntries) {
resourceObserverCallback(_tracking, resource);
}
performance.clearResourceTimings();
});
return observer;
}
这需要一个很长的答案。 工作游乐场示例链接位于最后。
要测试某些东西,您必须从外部隔离并注入所有外部依赖项。有不同的模式可以做到这一点。
您想要测试两件事 - 构造函数中发生的情况以及启动回调时发生的情况。我们将分别解决这些问题。 要测试您的
PerformanceObserverCallbackHandler
,您需要将其分离可调用的东西,并注入所有外部依赖项。
我们将隔离并隐藏 PerformanceObserver 在单例工厂后面,以便我们可以控制注入哪个实现以及何时注入。
这里是工厂的实现
class PerformanceObserverFactory {
private instance: PerformanceObserver | undefined;
create(callBack: PerformanceObserverCallback): PerformanceObserver {
if (this.instance) {
return this.instance;
}
return new PerformanceObserver(callBack);
}
setMockedIsntance(mock: PerformanceObserver) {
if (this.instance !== undefined) {
throw new Error(
'Mocked instance is already set. Consider calling reset() in beforeEach()'
);
}
this.instance = mock;
}
reset() {
this.instance = undefined;
}
}
const instance = new PerformanceObserverFactory();
export default instance;
现在,让我们拆开班级吧。一些外部依赖项没有被描述,所以我伪造了它们。请参阅
declare global
部分。这是消费者类的实现
import PerformanceObserverFactory from './PerformanceObserverFactory';
export type Handler = (
tracking: string,
resource: PerformanceResourceTiming
) => void;
declare global {
var _tracking: string;
var resourceObserverCallback: Handler;
}
export class ClassToBeTested {
private resourceObserver: PerformanceObserver;
constructor() {
const callBack = this.getPerformanceObserverCallback(
_tracking,
resourceObserverCallback,
performance
);
this.resourceObserver = PerformanceObserverFactory.create(callBack);
this.resourceObserver.observe({ entryTypes: ['resource'] });
}
getPerformanceObserverCallback(
tracking: string,
handler: Handler,
performance: Performance
): PerformanceObserverCallback {
return (list) => {
const resourceEntries = list.getEntriesByType(
'resource'
) as PerformanceResourceTiming[];
for (const resource of resourceEntries) {
handler(tracking, resource);
}
performance.clearResourceTimings();
};
}
}
最后,我们有测试本身:
import { ClassToBeTested, Handler } from './ClassToBeTested';
import PerformanceObserverFactory from './PerformanceObserverFactory';
import { mock } from 'jest-mock-extended';
describe('ClassToBeTested.constructor', () => {
beforeEach(() => {
PerformanceObserverFactory.reset();
});
it('Should subscribe to PerformanceObserver with correct params', () => {
const observeMock = jest.fn();
PerformanceObserverFactory.setMockedIsntance({
observe: observeMock,
} as unknown as PerformanceObserver);
const testSubject = new ClassToBeTested();
expect(observeMock).toHaveBeenCalledWith({ entryTypes: ['resource'] });
});
});
describe('ClassToBeTested.getPerformanceObserverCallback', () => {
beforeEach(() => {
PerformanceObserverFactory.reset();
});
it('Should call the resourceObserverCallback for eeach list entry and then call clearResourceTimings once', () => {
const entryListResult: Array<PerformanceResourceTiming> = [
{ name: 'name 1' },
{ name: 'name 2' },
] as Array<PerformanceResourceTiming>;
const resourceObserverCallbackMock = jest.fn();
const _tracking = 'I do not know what this is';
const performanceMock = mock<Performance>();
const entryList = mock<PerformanceObserverEntryList>();
entryList.getEntriesByType.mockReturnValue(entryListResult);
PerformanceObserverFactory.setMockedIsntance({
observe: jest.fn(),
} as unknown as PerformanceObserver);
const testSubject = new ClassToBeTested();
const theCallback = testSubject.getPerformanceObserverCallback(
_tracking,
resourceObserverCallbackMock,
performanceMock
);
//lets call it to test it
theCallback(entryList, mock<PerformanceObserver>());
expect(entryList.getEntriesByType).toHaveBeenCalledTimes(1);
expect(resourceObserverCallbackMock).toHaveBeenCalledTimes(
entryListResult.length
);
expect(performanceMock.clearResourceTimings).toHaveBeenCalled();
// we check that each item in the result list is processed
entryListResult.forEach((item, index) => {
expect(resourceObserverCallbackMock).toHaveBeenNthCalledWith(
index + 1,
_tracking,
item
);
});
});
});