如何在 Jest 中模拟 navigator.clipboard.writeText()?
Posted
技术标签:
【中文标题】如何在 Jest 中模拟 navigator.clipboard.writeText()?【英文标题】:How to mock navigator.clipboard.writeText() in Jest? 【发布时间】:2020-10-02 17:21:16 【问题描述】:在查看 Jest 问题和 SO answers 后,我尝试了以下 4 个选项,但我遇到了 TypeScript 错误或运行时错误。我真的很想让选项 1 (spyOn) 工作。
// ------ option 1 -----
// Gives this runtime error: "Cannot spyOn on a primitive value; undefined given"
const writeText = jest.spyOn(navigator.clipboard, 'writeText');
// ------ option 2 -----
Object.defineProperty(navigator, 'clipboard',
writeText: jest.fn(),
);
// ------ option 3 -----
// This is from SO answer but gives a TypeScript error
window.__defineGetter__('navigator', function()
return
clipboard:
writeText: jest.fn(x => x)
)
// ------ option 4 -----
const mockClipboard =
writeText: jest.fn()
;
global.navigator.clipboard = mockClipboard;
【问题讨论】:
【参考方案1】:在我的环境中,testing-library 苗条和开玩笑的 jsdom,我没有设法模拟 global.navigator
。有效的解决方案是在我的测试中模拟 window.navigator
。
describe('my-test', () =>
it("should copy to clipboard", () =>
const getByRole = render(MyComponent);
Object.assign(window.navigator,
clipboard:
writeText: jest.fn().mockImplementation(() => Promise.resolve()),
,
);
const button = getByRole("button");
fireEvent.click(button);
expect(window.navigator.clipboard.writeText)
.toHaveBeenCalledWith('the text that needs to be copied');
);
);
【讨论】:
【参考方案2】:我扩展了早期的解决方案,还为readText
提供了模拟剪贴板功能,因此可以测试剪贴板的内容。
这是我的test.js
文件的全部内容
import copyStringToClipboard from 'functions/copy-string-to-clipboard.js';
// ------- Mock -------
//Solution for mocking clipboard so it can be tested credit: <link to this post>
const originalClipboard = ...global.navigator.clipboard ;
beforeEach(() =>
let clipboardData = '' //initalizing clipboard data so it can be used in testing
const mockClipboard =
writeText: jest.fn(
(data) => clipboardData = data
),
readText: jest.fn(
() => return clipboardData
),
;
global.navigator.clipboard = mockClipboard;
);
afterEach(() =>
jest.resetAllMocks();
global.navigator.clipboard = originalClipboard;
);
// --------------------
it("copies a string to the clipboard", async () =>
//arrange
const string = 'test ?'
//act
copyStringToClipboard(string)
//assert
expect(navigator.clipboard.readText()).toBe(string)
expect(navigator.clipboard.writeText).toBeCalledTimes(1);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(string);
);
【讨论】:
【参考方案3】:我也遇到过类似的情况,用下面的方法在导航器对象中模拟剪贴板:
const originalClipboard = ...global.navigator.clipboard ;
const mockData =
"name": "Test Name",
"otherKey": "otherValue"
beforeEach(() =>
const mockClipboard =
writeText: jest.fn(),
;
global.navigator.clipboard = mockClipboard;
);
afterEach(() =>
jest.resetAllMocks();
global.navigator.clipboard = originalClipboard;
);
test("copies data to the clipboard", () =>
copyData(); //my method in the source code which uses the clipboard
expect(navigator.clipboard.writeText).toBeCalledTimes(1);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
JSON.stringify(mockData)
);
);
【讨论】:
谢谢。这个解决方案很完美! 谢谢。这个解决方案奏效了。 ??【参考方案4】:Jest 测试在 JSdom 环境中运行,并没有定义所有的属性,但是你应该在监视它之前定义函数。
这是一个例子:
Object.assign(navigator,
clipboard:
writeText: () => ,
,
);
describe("Clipboard", () =>
describe("writeText", () =>
jest.spyOn(navigator.clipboard, "writeText");
beforeAll(() =>
yourImplementationThatWouldInvokeClipboardWriteText();
);
it("should call clipboard.writeText", () =>
expect(navigator.clipboard.writeText).toHaveBeenCalledWith("zxc");
);
);
);
编辑:你也可以使用Object.defineProperty
,但它接受描述符对象作为第三个参数
Object.defineProperty(navigator, "clipboard",
value:
writeText: () => ,
,
);
【讨论】:
谢谢@Teneff。这是一个很好的玩笑和 jsdom 教育! 我不得不像这样调整Object.assign(navigator, clipboard: writeText: jest.fn().mockImplementation(() => Promise.resolve()), , );
@gawkface 你可能想发布一个单独的答案是你认为它可以帮助某人
@Teneff 我没有发布单独的答案,因为您的答案总体上对我有用,这个小调整可能在您的答案之上的某些情况下有用,但感谢您的提醒! 以上是关于如何在 Jest 中模拟 navigator.clipboard.writeText()?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Jest 中模拟 .then() 和 .catch()? [关闭]