模拟
该服务允许通过类似Vitest的接口模拟 Electron API 功能。
浏览器实用程序方法
mock
当提供 API 名称和函数名称时,模拟 Electron API 功能。将返回一个模拟对象。
例如,在规范文件中
const mockedShowOpenDialog = await browser.electron.mock('dialog', 'showOpenDialog');
await browser.electron.execute(
async (electron) =>
await electron.dialog.showOpenDialog({
properties: ['openFile', 'openDirectory'],
}),
);
expect(mockedShowOpenDialog).toHaveBeenCalledTimes(1);
expect(mockedShowOpenDialog).toHaveBeenCalledWith({
properties: ['openFile', 'openDirectory'],
});
mockAll
同时模拟 Electron API 上的所有函数,模拟作为对象返回
const { showOpenDialog, showMessageBox } = await browser.electron.mockAll('dialog');
await showOpenDialog.mockReturnValue('I opened a dialog!');
await showMessageBox.mockReturnValue('I opened a message box!');
clearAllMocks
在每个活动模拟上调用mockClear
const mockSetName = await browser.electron.mock('app', 'setName');
const mockWriteText = await browser.electron.mock('clipboard', 'writeText');
await browser.electron.execute((electron) => electron.app.setName('new app name'));
await browser.electron.execute((electron) => electron.clipboard.writeText('text to be written'));
await browser.electron.clearAllMocks();
expect(mockSetName.mock.calls).toStrictEqual([]);
expect(mockWriteText.mock.calls).toStrictEqual([]);
传递 apiName 字符串将清除该特定 API 的模拟
const mockSetName = await browser.electron.mock('app', 'setName');
const mockWriteText = await browser.electron.mock('clipboard', 'writeText');
await browser.electron.execute((electron) => electron.app.setName('new app name'));
await browser.electron.execute((electron) => electron.clipboard.writeText('text to be written'));
await browser.electron.clearAllMocks('app');
expect(mockSetName.mock.calls).toStrictEqual([]);
expect(mockWriteText.mock.calls).toStrictEqual([['text to be written']]);
resetAllMocks
在每个活动模拟上调用mockReset
const mockGetName = await browser.electron.mock('app', 'getName');
const mockReadText = await browser.electron.mock('clipboard', 'readText');
await mockGetName.mockReturnValue('mocked appName');
await mockReadText.mockReturnValue('mocked clipboardText');
await browser.electron.resetAllMocks();
const appName = await browser.electron.execute((electron) => electron.app.getName());
const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText());
expect(appName).toBeUndefined();
expect(clipboardText).toBeUndefined();
传递 apiName 字符串将重置该特定 API 的模拟
const mockGetName = await browser.electron.mock('app', 'getName');
const mockReadText = await browser.electron.mock('clipboard', 'readText');
await mockGetName.mockReturnValue('mocked appName');
await mockReadText.mockReturnValue('mocked clipboardText');
await browser.electron.resetAllMocks('app');
const appName = await browser.electron.execute((electron) => electron.app.getName());
const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText());
expect(appName).toBeUndefined();
expect(clipboardText).toBe('mocked clipboardText');
restoreAllMocks
在每个活动模拟上调用mockRestore
const mockGetName = await browser.electron.mock('app', 'getName');
const mockReadText = await browser.electron.mock('clipboard', 'readText');
await mockGetName.mockReturnValue('mocked appName');
await mockReadText.mockReturnValue('mocked clipboardText');
await browser.electron.restoreAllMocks();
const appName = await browser.electron.execute((electron) => electron.app.getName());
const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText());
expect(appName).toBe('my real app name');
expect(clipboardText).toBe('some real clipboard text');
传递 apiName 字符串将恢复该特定 API 的模拟
const mockGetName = await browser.electron.mock('app', 'getName');
const mockReadText = await browser.electron.mock('clipboard', 'readText');
await mockGetName.mockReturnValue('mocked appName');
await mockReadText.mockReturnValue('mocked clipboardText');
await browser.electron.restoreAllMocks('app');
const appName = await browser.electron.execute((electron) => electron.app.getName());
const clipboardText = await browser.electron.execute((electron) => electron.clipboard.readText());
expect(appName).toBe('my real app name');
expect(clipboardText).toBe('mocked clipboardText');
isMockFunction
检查给定参数是否为 Electron 模拟函数。如果您使用的是 TypeScript,它还将缩小其类型。
const mockGetName = await browser.electron.mock('app', 'getName');
expect(browser.electron.isMockFunction(mockGetName)).toBe(true);
模拟对象
每个模拟对象都提供以下方法
mockImplementation
接受用作模拟实现的函数。
const mockGetName = await browser.electron.mock('app', 'getName');
let callsCount = 0;
await mockGetName.mockImplementation(() => {
// callsCount is not accessible in the electron context so we need to guard it
if (typeof callsCount !== 'undefined') {
callsCount++;
}
return 'mocked value';
});
const result = await browser.electron.execute(async (electron) => await electron.app.getName());
expect(callsCount).toBe(1);
expect(result).toBe('mocked value');
mockImplementationOnce
接受一个函数,该函数将在下一次调用期间用作模拟的实现。如果链接,每次连续调用都会产生不同的结果。
当模拟函数用完实现后,它将调用使用mockImplementation
设置的默认实现。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockImplementationOnce(() => 'first mock');
await mockGetName.mockImplementationOnce(() => 'second mock');
let name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe('first mock');
name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe('second mock');
name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBeNull();
mockReturnValue
接受一个值,每当调用模拟函数时都会返回该值。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockReturnValue('mocked name');
const name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe('mocked name');
mockReturnValueOnce
接受一个值,该值将在下一次函数调用期间返回。如果链接,每次连续调用都将返回指定的值。
当没有更多mockReturnValueOnce
值可用时,模拟将回退到先前定义的实现(如果有)。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockReturnValueOnce('first mock');
await mockGetName.mockReturnValueOnce('second mock');
let name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe('first mock');
name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe('second mock');
name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBeNull();
mockResolvedValue
接受一个值,每当调用模拟函数时都会解析该值。
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
await mockGetFileIcon.mockResolvedValue('This is a mock');
const fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon'));
expect(fileIcon).toBe('This is a mock');
mockResolvedValueOnce
接受一个值,该值将在下一次函数调用期间解析。如果链接,每次连续调用都将解析指定的值。
当没有更多mockResolvedValueOnce
值可用时,模拟将回退到先前定义的实现(如果有)。
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
await mockGetFileIcon.mockResolvedValue('default mocked icon');
await mockGetFileIcon.mockResolvedValueOnce('first mocked icon');
await mockGetFileIcon.mockResolvedValueOnce('second mocked icon');
let fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon'));
expect(fileIcon).toBe('first mocked icon');
fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon'));
expect(fileIcon).toBe('second mocked icon');
fileIcon = await browser.electron.execute(async (electron) => await electron.app.getFileIcon('/path/to/icon'));
expect(fileIcon).toBe('default mocked icon');
mockRejectedValue
接受一个值,每当调用模拟函数时都会拒绝该值。
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
await mockGetFileIcon.mockRejectedValue('This is a mock error');
const fileIconError = await browser.electron.execute(async (electron) => {
try {
await electron.app.getFileIcon('/path/to/icon');
} catch (e) {
return e;
}
});
expect(fileIconError).toBe('This is a mock error');
mockRejectedValueOnce
接受一个值,该值将在下一次函数调用期间被拒绝。如果链接,每次连续调用都将拒绝指定的值。
当没有更多mockRejectedValueOnce
值可用时,模拟将回退到先前定义的实现(如果有)。
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
await mockGetFileIcon.mockRejectedValue('default mocked icon error');
await mockGetFileIcon.mockRejectedValueOnce('first mocked icon error');
await mockGetFileIcon.mockRejectedValueOnce('second mocked icon error');
const getFileIcon = async () =>
await browser.electron.execute(async (electron) => {
try {
await electron.app.getFileIcon('/path/to/icon');
} catch (e) {
return e;
}
});
let fileIcon = await getFileIcon();
expect(fileIcon).toBe('first mocked icon error');
fileIcon = await getFileIcon();
expect(fileIcon).toBe('second mocked icon error');
fileIcon = await getFileIcon();
expect(fileIcon).toBe('default mocked icon error');
mockClear
清除模拟的 Electron API 函数的历史记录。模拟实现不会重置。
const mockGetName = await browser.electron.mock('app', 'getName');
await browser.electron.execute((electron) => electron.app.getName());
await mockGetName.mockClear();
await browser.electron.execute((electron) => electron.app.getName());
expect(mockGetName).toHaveBeenCalledTimes(1);
mockReset
重置模拟的 Electron API 函数。模拟历史记录将被清除,实现将重置为空函数(返回未定义)。
这也重置所有“一次”实现。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockReturnValue('mocked name');
await browser.electron.execute((electron) => electron.app.getName());
await mockGetName.mockReset();
const name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBeUndefined();
expect(mockGetName).toHaveBeenCalledTimes(1);
mockRestore
将原始实现恢复到 Electron API 函数。
const appName = await browser.electron.execute((electron) => electron.app.getName());
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockReturnValue('mocked name');
await mockGetName.mockRestore();
const name = await browser.electron.execute((electron) => electron.app.getName());
expect(name).toBe(appName);
withImplementation
在回调执行期间临时覆盖原始模拟实现。electron 对象以与 execute
相同的方式传递到回调中。
const mockGetName = await browser.electron.mock('app', 'getName');
const withImplementationResult = await mockGetName.withImplementation(
() => 'temporary mock name',
(electron) => electron.app.getName(),
);
expect(withImplementationResult).toBe('temporary mock name');
它也可以与异步回调一起使用
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
const withImplementationResult = await mockGetFileIcon.withImplementation(
() => Promise.resolve('temporary mock icon'),
async (electron) => await electron.app.getFileIcon('/path/to/icon'),
);
expect(withImplementationResult).toBe('temporary mock icon');
getMockImplementation
如果存在,则返回当前模拟实现。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockImplementation(() => 'mocked name');
const mockImpl = mockGetName.getMockImplementation();
expect(mockImpl()).toBe('mocked name');
getMockName
返回模拟的分配名称。默认为 electron.<apiName>.<funcName>
。
const mockGetName = await browser.electron.mock('app', 'getName');
expect(mockGetName.getMockName()).toBe('electron.app.getName');
mockName
为模拟分配名称。可以通过getMockName
检索名称。
const mockGetName = await browser.electron.mock('app', 'getName');
mockGetName.mockName('test mock');
expect(mockGetName.getMockName()).toBe('test mock');
mockReturnThis
如果您需要从方法返回this
上下文而不调用实现,则很有用。这是以下内容的简写形式:
await spy.mockImplementation(function () {
return this;
});
...这使得 API 函数能够链接
const mockGetName = await browser.electron.mock('app', 'getName');
const mockGetVersion = await browser.electron.mock('app', 'getVersion');
await mockGetName.mockReturnThis();
await browser.electron.execute((electron) => electron.app.getName().getVersion());
expect(mockGetVersion).toHaveBeenCalled();
mock.calls
这是一个数组,包含每次调用的所有参数。数组的每个项目都是该调用的参数。
const mockGetFileIcon = await browser.electron.mock('app', 'getFileIcon');
await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/icon'));
await browser.electron.execute((electron) => electron.app.getFileIcon('/path/to/another/icon', { size: 'small' }));
expect(mockGetFileIcon.mock.calls).toStrictEqual([
['/path/to/icon'], // first call
['/path/to/another/icon', { size: 'small' }], // second call
]);
mock.lastCall
这包含最后一次调用的参数。如果模拟未被调用,它将返回undefined
。
const mockSetName = await browser.electron.mock('app', 'setName');
await browser.electron.execute((electron) => electron.app.setName('test'));
expect(mockSetName.mock.lastCall).toStrictEqual(['test']);
await browser.electron.execute((electron) => electron.app.setName('test 2'));
expect(mockSetName.mock.lastCall).toStrictEqual(['test 2']);
await browser.electron.execute((electron) => electron.app.setName('test 3'));
expect(mockSetName.mock.lastCall).toStrictEqual(['test 3']);
mock.results
这是一个数组,包含从模拟返回的所有值。数组的每个项目都是一个具有属性 type 和 value 的对象。可用类型为
'return' - 模拟返回而不抛出。'throw' - 模拟抛出一个值。
value 属性包含返回值或抛出的错误。如果模拟返回了一个 Promise,则该值将是解析后的值,而不是 Promise 本身,除非它从未被解析。
const mockGetName = await browser.electron.mock('app', 'getName');
await mockGetName.mockImplementationOnce(() => 'result');
await mockGetName.mockImplementation(() => {
throw new Error('thrown error');
});
await expect(browser.electron.execute((electron) => electron.app.getName())).resolves.toBe('result');
await expect(browser.electron.execute((electron) => electron.app.getName())).rejects.toThrow('thrown error');
expect(mockGetName.mock.results).toStrictEqual([
{
type: 'return',
value: 'result',
},
{
type: 'throw',
value: new Error('thrown error'),
},
]);
mock.invocationCallOrder
模拟调用的顺序。这将返回一个在所有定义的模拟之间共享的数字数组。如果模拟从未被调用,则将返回一个空数组。
const mockGetName = await browser.electron.mock('app', 'getName');
const mockGetVersion = await browser.electron.mock('app', 'getVersion');
await browser.electron.execute((electron) => electron.app.getName());
await browser.electron.execute((electron) => electron.app.getVersion());
await browser.electron.execute((electron) => electron.app.getName());
expect(mockGetName.mock.invocationCallOrder).toStrictEqual([1, 3]);
expect(mockGetVersion.mock.invocationCallOrder).toStrictEqual([2]);