Skip to content

Commit f3a6dbe

Browse files
authored
Refactor error handling to extract missing module names from error messages (#25785)
fixes #25786
1 parent bd9b412 commit f3a6dbe

File tree

2 files changed

+68
-25
lines changed

2 files changed

+68
-25
lines changed

src/client/testing/testController/common/utils.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -175,27 +175,36 @@ export async function startDiscoveryNamedPipe(
175175
}
176176

177177
/**
178-
* Detects if an error message indicates that pytest is not installed.
179-
* @param message The error message to check
180-
* @returns True if the error indicates pytest is not installed
178+
* Extracts the missing module name from a ModuleNotFoundError or ImportError message.
179+
* @param message The error message to parse
180+
* @returns The module name if found, undefined otherwise
181181
*/
182-
function isPytestNotInstalledError(message: string): boolean {
183-
return (
184-
(message.includes('ModuleNotFoundError') && message.includes('pytest')) ||
185-
(message.includes('No module named') && message.includes('pytest')) ||
186-
(message.includes('ImportError') && message.includes('pytest'))
187-
);
182+
function extractMissingModuleName(message: string): string | undefined {
183+
// Match patterns like:
184+
// - No module named 'requests'
185+
// - No module named "requests"
186+
// - ModuleNotFoundError: No module named 'requests'
187+
// - ImportError: No module named requests
188+
const patterns = [/No module named ['"]([^'"]+)['"]/, /No module named (\S+)/];
189+
190+
for (const pattern of patterns) {
191+
const match = message.match(pattern);
192+
if (match) {
193+
return match[1];
194+
}
195+
}
196+
return undefined;
188197
}
189198

190199
export function buildErrorNodeOptions(uri: Uri, message: string, testType: string): ErrorTestItemOptions {
191200
let labelText = testType === 'pytest' ? 'pytest Discovery Error' : 'Unittest Discovery Error';
192201
let errorMessage = message;
193202

194-
// Provide more specific error message if pytest is not installed
195-
if (testType === 'pytest' && isPytestNotInstalledError(message)) {
196-
labelText = 'pytest Not Installed';
197-
errorMessage =
198-
'pytest is not installed in the selected Python environment. Please install pytest to enable test discovery and execution.';
203+
// Check for missing module errors and provide specific messaging
204+
const missingModule = extractMissingModuleName(message);
205+
if (missingModule) {
206+
labelText = `Missing Module: ${missingModule}`;
207+
errorMessage = `The module '${missingModule}' is not installed in the selected Python environment. Please install it to enable test discovery.`;
199208
}
200209

201210
return {

src/test/testing/testController/common/buildErrorNodeOptions.unit.test.ts

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,56 @@ import { expect } from 'chai';
55
import { Uri } from 'vscode';
66
import { buildErrorNodeOptions } from '../../../../client/testing/testController/common/utils';
77

8-
suite('buildErrorNodeOptions - pytest not installed detection', () => {
8+
suite('buildErrorNodeOptions - missing module detection', () => {
99
const workspaceUri = Uri.file('/test/workspace');
1010

11-
test('Should detect pytest ModuleNotFoundError and provide specific message', () => {
11+
test('Should detect pytest ModuleNotFoundError and show missing module label', () => {
1212
const errorMessage =
1313
'Traceback (most recent call last):\n File "<string>", line 1, in <module>\n import pytest\nModuleNotFoundError: No module named \'pytest\'';
1414

1515
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'pytest');
1616

17-
expect(result.label).to.equal('pytest Not Installed [workspace]');
17+
expect(result.label).to.equal('Missing Module: pytest [workspace]');
1818
expect(result.error).to.equal(
19-
'pytest is not installed in the selected Python environment. Please install pytest to enable test discovery and execution.',
19+
"The module 'pytest' is not installed in the selected Python environment. Please install it to enable test discovery.",
2020
);
2121
});
2222

23-
test('Should detect pytest ImportError and provide specific message', () => {
23+
test('Should detect pytest ImportError and show missing module label', () => {
2424
const errorMessage = 'ImportError: No module named pytest';
2525

2626
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'pytest');
2727

28-
expect(result.label).to.equal('pytest Not Installed [workspace]');
28+
expect(result.label).to.equal('Missing Module: pytest [workspace]');
2929
expect(result.error).to.equal(
30-
'pytest is not installed in the selected Python environment. Please install pytest to enable test discovery and execution.',
30+
"The module 'pytest' is not installed in the selected Python environment. Please install it to enable test discovery.",
3131
);
3232
});
3333

34-
test('Should use generic error for non-pytest-related errors', () => {
34+
test('Should detect other missing modules and show module name in label', () => {
35+
const errorMessage =
36+
"bob\\test_bob.py:3: in <module>\n import requests\nE ModuleNotFoundError: No module named 'requests'\n=========================== short test summary info";
37+
38+
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'pytest');
39+
40+
expect(result.label).to.equal('Missing Module: requests [workspace]');
41+
expect(result.error).to.equal(
42+
"The module 'requests' is not installed in the selected Python environment. Please install it to enable test discovery.",
43+
);
44+
});
45+
46+
test('Should detect missing module with double quotes', () => {
47+
const errorMessage = 'ModuleNotFoundError: No module named "numpy"';
48+
49+
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'pytest');
50+
51+
expect(result.label).to.equal('Missing Module: numpy [workspace]');
52+
expect(result.error).to.equal(
53+
"The module 'numpy' is not installed in the selected Python environment. Please install it to enable test discovery.",
54+
);
55+
});
56+
57+
test('Should use generic error for non-module-related errors', () => {
3558
const errorMessage = 'Some other error occurred';
3659

3760
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'pytest');
@@ -40,12 +63,23 @@ suite('buildErrorNodeOptions - pytest not installed detection', () => {
4063
expect(result.error).to.equal('Some other error occurred');
4164
});
4265

43-
test('Should use generic error for unittest errors', () => {
44-
const errorMessage = "ModuleNotFoundError: No module named 'pytest'";
66+
test('Should detect missing module for unittest errors', () => {
67+
const errorMessage = "ModuleNotFoundError: No module named 'pandas'";
68+
69+
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'unittest');
70+
71+
expect(result.label).to.equal('Missing Module: pandas [workspace]');
72+
expect(result.error).to.equal(
73+
"The module 'pandas' is not installed in the selected Python environment. Please install it to enable test discovery.",
74+
);
75+
});
76+
77+
test('Should use generic error for unittest non-module errors', () => {
78+
const errorMessage = 'Some other error occurred';
4579

4680
const result = buildErrorNodeOptions(workspaceUri, errorMessage, 'unittest');
4781

4882
expect(result.label).to.equal('Unittest Discovery Error [workspace]');
49-
expect(result.error).to.equal("ModuleNotFoundError: No module named 'pytest'");
83+
expect(result.error).to.equal('Some other error occurred');
5084
});
5185
});

0 commit comments

Comments
 (0)