跳至主要内容

模拟

该服务允许通过类似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]);

欢迎!我如何提供帮助?

WebdriverIO AI Copilot