模擬
此服務允許透過類似 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]);