vscode-test: unable to import vscode in test using jest
Hi, I’m trying to run some integration tests for my vscode extension using jest. Unit tests work fine since I’m able to mock vscode however, when I’m trying to run an integration test I’m getting Cannot find module 'vscode'
in the files when i’m using vscode api (import * as vscode from 'vscode'
)
jest config looks like this:
module.exports = {
moduleFileExtensions: ['js'],
testMatch: ['<rootDir>/out/test/**/*.int.test.js'],
verbose: true,
};
runTest.ts
import * as path from 'path';
import { runTests } from 'vscode-test';
async function main() {
try {
// The folder containing the Extension Manifest package.json
// Passed to `--extensionDevelopmentPath`
const extensionDevelopmentPath = path.resolve(__dirname, '../../');
// The path to the extension test script
// Passed to --extensionTestsPath
const extensionTestsPath = path.resolve(__dirname, './suite/index');
// Download VS Code, unzip it and run the integration test
console.log(`running tests... ext path: ${extensionTestsPath}`);
await runTests({
extensionDevelopmentPath,
extensionTestsPath,
launchArgs: ['--disable-extensions']
});
} catch (err) {
console.error('Failed to run tests', err);
process.exit(1);
}
}
main();
index.ts
import * as path from 'path';
import { runCLI } from 'jest-cli';
import * as vscode from 'vscode';
export function run(): Promise<void> {
return new Promise((resolve, reject) => {
const projectRootPath = path.join(__dirname, '../../../');
const config = path.join(projectRootPath, 'jest.e2e.config.js');
vscode.window.showInformationMessage('Run suite');
runCLI({ config } as any, [projectRootPath])
.then(jestCliCallResult => {
console.log(`complete: ${JSON.stringify(jestCliCallResult)}`);
console.log('print results');
jestCliCallResult.results.testResults
.forEach(testResult => {
testResult.testResults
.filter(assertionResult => assertionResult.status === 'passed')
.forEach(({ ancestorTitles, title, status }) => {
console.info(` ● ${ancestorTitles} › ${title} (${status})`);
});
});
console.log('check results');
jestCliCallResult.results.testResults
.forEach(testResult => {
if (testResult.failureMessage) {
console.error(testResult.failureMessage);
}
});
resolve();
})
.catch(errorCaughtByJestRunner => {
console.error('error in test runner', errorCaughtByJestRunner);
reject(errorCaughtByJestRunner);
});
});
}
I installed both vscode-test
and @types/vscode
as dev dependencies. I’m not sure why my tests are unable to find the ‘vscode’ dependency. Vscode is the only dependency that has this issue, other modules work fine. Can anyone help?
Thanks!
About this issue
- Original URL
- State: open
- Created 5 years ago
- Reactions: 8
- Comments: 27 (1 by maintainers)
I found a very slim workaround!
I was also struggling like hell with that. I found that article which got me on the right track: https://www.richardkotze.com/coding/unit-test-mock-vs-code-extension-api-jest
However to make it run in the default TypeScript extension workflow you need to put the
__mocks__
folder (yes plural, not singular as stated in the article) under yourrootDir
as defined in thetsconfig.json
. In the default extension boilerplate it’s./src
.Within the
__mocks__
folder you create avscode.ts
(not.js
as described in the article) with the following content.In my case an empty
vscode
object was enough since I only use types and interfaces from thevscode
namespace. I would recommend the same for all of you for the major parts of the extension logic. Interacting with thevscode
API can be limited to theextension.ts
which is not unit tested but covered by e.g. end-to-end-tests.So I actually manage to inject vscode into jest, and it’s not that ugly. This works for me, hope it works for someone else too!
First of all, create your own vscode environment file
next, make jest use this environment, I did it by adding it into my jest config
now we are just missing the tricky and most important part. Create a file, call it whatever you want, I called it
vscode.js
and copy this lineFinal step, go to your jest config, and define a module mapper like this
my vscode file is in test-jest/vscode.js place yours wherever you need!
By doing this, any line that is importing vscode will really import it into jest, so you are able to run methods, mock them or do whatever you want because you have the real vscode object!
I hope this helps someone!!
[EDITED]
Forgot to mention you also need to define a custom Vscode jest runner if someone needs this too I’ll add it to this comment!
I was able to reference
vscode
instance in my tests only throughprocess
variable, maybe I was doing something wrong with the globals setup butvscode
package couldn’t be required there as @octref proposed.So to make it work with
process
variable in typescript you have to define your typing anywhere in your project as a separate file ex.process.d.ts
Preview:Then in
suite/index.ts
importvscode
and setprocess.vscode
, beforerun
function definition Preview:And finally in your test file you can reference
process.vscode
Preview:It is very hacky way of accessing vscode instance, so try to find a better solution. I just post my findings FYI.
To address this issue, I created jest-mock-vscode. It can help with testing logic using unit tests. It is not designed for E2E/UI testing. The vscode-test runner is best for that.
The problem here is you are using stubs/mocks, so you can’t do actual integration/E2E/UI tests and verify things work in vscode instance itself, or verify the API hasn’t changed. That’s the real benefit of using vscode-test here vs. just mocking/stubbing out all the external calls.
Adding a mocks folder for vscode per blog article here is a clean/efficient solution: https://www.richardkotze.com/coding/unit-test-mock-vs-code-extension-api-jest
https://github.com/rkotze/git-mob-vs-code/blob/baf86ae15bf359bf409a6a3bdc7ac74850640433/__mocks__/vscode.js
@vinnichase You have a flaw there. The way vscode extensions are meant to be tested is IN vscode precisely so that you test vscode fully integrated. This is also the way microsoft introduces you to extension testing… Otherwise any vscode update may blow up your extension because you forgot to update your mocks…
You also won’t be able to properly test by passing via the process module, because jest runs everything via the node “vm” module ( https://nodejs.org/api/vm.html ) which prevents you from selectively modifying the vscode object if the vscode object is not part of the vms global object (the vm context). That is you will be able to modify it IN the test but your modifications to the vscode module will NOT propagate outwards to your extension code, because of the vm. It does not matter that the process PID is the same. The key point is the “vm” module that jest uses.
The correct way to use jest inside a vscode host is you do
and then
You still wont be able to IMPORT vscode like normal in your tests, which is annoying with those import quickfixes, but vscode is available as a global now and now you can mock those pesky extensions error messages etc. and changes will propagate outwards
Thanks @MarioCadenas, but just a few hours after posting that I started to consider other options (e.g. just using Mocha instead). I have little experience in this area, so I think I’ll do some reading of my own before coming back with more specific questions. If I never reply, it’s because I went with an alternative 😃 Thanks anyway!
Thanks for the hint! However this breaks my understanding of unit tests which I was doing in my case. Your approach would apply better in an integration test.
I didn’t even mock
vscode
functionality but only imported a class and used it as an interface and an enum.There used to be a special vscode typings package on npm for that purpose but maintenance stopped and it deviated from the ones exposed in the vscode extension context. So thats ridiculous that you can not use the interace description (which is by definition the abstraction of the running code) separately from the runtime context.