在单元测试中使用Jest模拟VS Code extension API

VS Code extension进行单元测试时通常会遇到一个问题,代码中所使用的VS Code编辑器的功能都依赖于vscode库,但是我们在单元测试中并没有添加对vscode库的依赖,所以导致运行单元测试时出错。由于vscode库是作为第三方依赖被引入到我们的VS Code extension中的,所以它并不受我们的控制,最好的办法就是在单元测试中对其中的API进行模拟。本文中我将介绍如何使用Jest来模拟vscode库的API。

如果你还不太熟悉如何开始创建一个VS Code extension,这里的文档可以教你快速上手。

创建好VS Code extension项目后,你会发现在根目录下有一个package.json文件,VS Code extension会从中读取配置项来管理UI界面元素,在实际开发中你可能会使用到其中的一些属性。我们可以通过package.json来设置项目所需要的依赖项,这里我们将Jest添加为dev dependency,并添加npm脚本以运行Jest单元测试。

npm i -D jest
 
  1. {

  2. "scripts": {

  3. "test": "jest"

  4. }

  5. }

模拟VS Code node module

Jest提供了一些mocking的选项,但是因为我们想要模拟整个vscode node module,所以最简单的办法是在与node_modules文件夹相同的位置(通常是项目的根目录)创建一个__mocks__文件夹,并在其中添加一个与要模拟的模块名称相同的文件(vscode.js)。

你不需要在测试代码中导入该模块,mock会自动加载它。Jest称此为manual mocks

这种方法最大的好处是它能将我们的测试代码与所依赖的模块分离,使测试代码看起来更加整洁。这里有一个小问题,新加入的开发者需要知道__mocks__文件夹,否则很难理解单元测试是如何正常工作的,因为单元测试中并没有VS Code模块被模拟的代码。

以下就是对VS Code模块进行模拟的代码。我们并没有模拟整个API,你可以根据需要进行调整。

 
  1. // vscode.js

  2. const languages = {

  3. createDiagnosticCollection: jest.fn()

  4. };

  5. const StatusBarAlignment = {};

  6. const window = {

  7. createStatusBarItem: jest.fn(() => ({

  8. show: jest.fn()

  9. })),

  10. showErrorMessage: jest.fn(),

  11. showWarningMessage: jest.fn(),

  12. createTextEditorDecorationType: jest.fn()

  13. };

  14. const workspace = {

  15. getConfiguration: jest.fn(),

  16. workspaceFolders: [],

  17. onDidSaveTextDocument: jest.fn()

  18. };

  19. const OverviewRulerLane = {

  20. Left: null

  21. };

  22. const Uri = {

  23. file: f => f,

  24. parse: jest.fn()

  25. };

  26. const Range = jest.fn();

  27. const Diagnostic = jest.fn();

  28. const DiagnosticSeverity = { Error: 0, Warning: 1, Information: 2, Hint: 3 };

  29. const debug = {

  30. onDidTerminateDebugSession: jest.fn(),

  31. startDebugging: jest.fn()

  32. };

  33. const commands = {

  34. executeCommand: jest.fn()

  35. };

  36. const vscode = {

  37. languages,

  38. StatusBarAlignment,

  39. window,

  40. workspace,

  41. OverviewRulerLane,

  42. Uri,

  43. Range,

  44. Diagnostic,

  45. DiagnosticSeverity,

  46. debug,

  47. commands

  48. };

  49. module.exports = vscode;

使用模拟的VS Code模块的示例

我的开源项目Git Mob for VS code中使用了这种方法,我将用其中的代码来说明如何使用模拟的VS Code模块。

下面的例子中,VS Code编辑器的状态栏会根据Git钩子prepare-commit-msg是否被调用来做相应的调整,你可以看到这里我并没有将vscode模块导入到我的测试文件中并对其进行模拟。

 
  1. // git-mob-hook-status.spec.js

  2. const { hasPrepareCommitMsgTemplate } = require("../prepare-commit-msg-file");

  3. const { gitMobHookStatus } = require("./git-mob-hook-status");

  4. jest.mock("./../prepare-commit-msg-file");

  5. describe("Hook or template status", function() {

  6. let mockContext;

  7. beforeAll(function() {

  8. mockContext = {

  9. subscriptions: []

  10. };

  11. });

  12. afterEach(function() {

  13. hasPrepareCommitMsgTemplate.mockReset();

  14. });

  15. it("using git template for co-authors", () => {

  16. hasPrepareCommitMsgTemplate.mockReturnValue(false);

  17. const statusBar = gitMobHookStatus({ context: mockContext })();

  18. expect(statusBar).toEqual(

  19. expect.objectContaining({

  20. text: "$(file-code) Git Mob",

  21. tooltip: "Using .gitmessage template"

  22. })

  23. );

  24. });

  25. it("using git prepare commit msg for co-authors", () => {

  26. hasPrepareCommitMsgTemplate.mockReturnValue(true);

  27. const statusBar = gitMobHookStatus({ context: mockContext })();

  28. expect(statusBar).toEqual(

  29. expect.objectContaining({

  30. text: "$(zap) Git Mob",

  31. tooltip: "Using prepare-commit-msg hook"

  32. })

  33. );

  34. });

  35. });

 
  1. // git-mob-hook-status.js

  2. const vscode = require("vscode");

  3. const { hasPrepareCommitMsgTemplate } = require("../prepare-commit-msg-file");

  4. function gitMobHookStatus({ context }) {

  5. const myStatusBarItem = vscode.window.createStatusBarItem(

  6. vscode.StatusBarAlignment.Left,

  7. 10

  8. );

  9. context.subscriptions.push(myStatusBarItem);

  10. return function() {

  11. myStatusBarItem.text = "$(file-code) Git Mob";

  12. myStatusBarItem.tooltip = "Using .gitmessage template";

  13. if (hasPrepareCommitMsgTemplate()) {

  14. myStatusBarItem.text = "$(zap) Git Mob";

  15. myStatusBarItem.tooltip = "Using prepare-commit-msg hook";

  16. }

  17. myStatusBarItem.show();

  18. return myStatusBarItem;

  19. };

  20. }

  21. exports.gitMobHookStatus = gitMobHookStatus;

你可以在这里查看源代码:

我能检查vscode模块中的方法是否被调用了吗?

你可以导入模拟的vscode模块。下面的代码中,我想要检查当用户修改co-author文件时onDidSaveTextDocument事件是否被订阅了。

 
  1. const vscode = require("../__mocks__/vscode");

  2. // ...

  3. test("Reload co-author list when git-coauthors file saved", () => {

  4. reloadOnSave(coAuthorProviderStub);

  5. expect(vscode.workspace.onDidSaveTextDocument).toHaveBeenCalledWith(

  6. expect.any(Function)

  7. );

  8. // ...

  9. });

  10. // ...

可以看到这里都是Jest mock API的标准用法,这意味着我们可以在代码中正常使用vscode模块的方法,而不受manual mock的任何限制。例如,我们还可以使用mockImplementation来修改实现。

更多示例可以查看这里的源代码:

编写单元测试最大的好处是可以快速得到反馈结果,如果你对TDD(Test-Driven Development,测试驱动开发)情有独钟,那么单元测试将使你对VS Code extension的开发更加信心满满。

 

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

 

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

 

  • 15
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值