跳至主要內容

模擬

此服務允許透過類似 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