Skip to content

Commit 34965e3

Browse files
committed
test fixes
1 parent ca14068 commit 34965e3

File tree

2 files changed

+147
-51
lines changed

2 files changed

+147
-51
lines changed

.github/instructions/testing-workflow.instructions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,3 +578,4 @@ envConfig.inspect
578578
- When mocking `testController.createTestItem()` in unit tests, use `typemoq.It.isAny()` for parameters when testing handler behavior (not ID/label generation logic), but consider using specific matchers (e.g., `It.is((id: string) => id.startsWith('_error_'))`) when the actual values being passed are important for correctness - this balances test precision with maintainability (2)
579579
- Remove unused variables from test code immediately - leftover tracking variables like `validationCallCount` that aren't referenced indicate dead code that should be simplified (1)
580580
- Use `Uri.file(path).fsPath` for both sides of path comparisons in tests to ensure cross-platform compatibility - Windows converts forward slashes to backslashes automatically (1)
581+
- When tests fail with "Cannot stub non-existent property", the method likely moved to a different class during refactoring - find the class that owns the method and test that class directly instead of stubbing on the original class (1)

src/test/testing/testController/controller.unit.test.ts

Lines changed: 146 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const vscodeApi = require('vscode') as typeof import('vscode');
1313
import { PYTEST_PROVIDER, UNITTEST_PROVIDER } from '../../../client/testing/common/constants';
1414
import * as envExtApiInternal from '../../../client/envExt/api.internal';
1515
import { getProjectId } from '../../../client/testing/testController/common/projectUtils';
16+
import * as projectUtils from '../../../client/testing/testController/common/projectUtils';
1617

1718
function createStubTestController(): TestController {
1819
const disposable = { dispose: () => undefined };
@@ -62,6 +63,8 @@ ensureVscodeTestsNamespace();
6263
// Dynamically require AFTER the vscode.tests namespace exists.
6364
// eslint-disable-next-line @typescript-eslint/no-var-requires
6465
const { PythonTestController } = require('../../../client/testing/testController/controller');
66+
// eslint-disable-next-line @typescript-eslint/no-var-requires
67+
const { TestProjectRegistry } = require('../../../client/testing/testController/common/testProjectRegistry');
6568

6669
suite('PythonTestController', () => {
6770
let sandbox: sinon.SinonSandbox;
@@ -143,7 +146,7 @@ suite('PythonTestController', () => {
143146
});
144147
});
145148

146-
suite('createDefaultProject', () => {
149+
suite('createDefaultProject (via TestProjectRegistry)', () => {
147150
test('creates a single default project using active interpreter', async () => {
148151
const workspaceUri: Uri = vscodeApi.Uri.file('/workspace/myws');
149152
const interpreter = {
@@ -153,16 +156,40 @@ suite('PythonTestController', () => {
153156
sysPrefix: '/opt/py',
154157
};
155158

156-
const controller = createController({ unittestEnabled: false, interpreter });
157-
158159
const fakeDiscoveryAdapter = { kind: 'discovery' };
159160
const fakeExecutionAdapter = { kind: 'execution' };
160-
sandbox
161-
.stub(controller as any, 'createTestAdapters')
162-
.returns({ discoveryAdapter: fakeDiscoveryAdapter, executionAdapter: fakeExecutionAdapter });
161+
sandbox.stub(projectUtils, 'createTestAdapters').returns({
162+
discoveryAdapter: fakeDiscoveryAdapter,
163+
executionAdapter: fakeExecutionAdapter,
164+
} as any);
165+
166+
// Stub useEnvExtension to return false so createDefaultProject is called
167+
sandbox.stub(envExtApiInternal, 'useEnvExtension').returns(false);
168+
169+
const interpreterService = {
170+
getActiveInterpreter: sandbox.stub().resolves(interpreter),
171+
} as any;
172+
173+
const configSettings = {
174+
getSettings: sandbox.stub().returns({
175+
testing: { unittestEnabled: false },
176+
}),
177+
} as any;
178+
179+
const testController = createStubTestController();
180+
const envVarsService = {} as any;
181+
182+
const registry = new TestProjectRegistry(
183+
testController,
184+
configSettings,
185+
interpreterService,
186+
envVarsService,
187+
);
163188

164-
const project = await (controller as any).createDefaultProject(workspaceUri);
189+
const projects = await registry.discoverAndRegisterProjects(workspaceUri);
190+
const project = projects[0];
165191

192+
assert.strictEqual(projects.length, 1);
166193
assert.strictEqual(project.workspaceUri.toString(), workspaceUri.toString());
167194
assert.strictEqual(project.projectUri.toString(), workspaceUri.toString());
168195
assert.strictEqual(project.projectId, getProjectId(workspaceUri));
@@ -181,29 +208,54 @@ suite('PythonTestController', () => {
181208
});
182209
});
183210

184-
suite('discoverWorkspaceProjects', () => {
211+
suite('discoverWorkspaceProjects (via TestProjectRegistry)', () => {
185212
test('respects useEnvExtension() == false and falls back to single default project', async () => {
186-
const controller = createController();
187213
const workspaceUri: Uri = vscodeApi.Uri.file('/workspace/a');
188214

189-
const defaultProject = { projectId: 'default', projectUri: workspaceUri };
190-
const createDefaultProjectStub = sandbox
191-
.stub(controller as any, 'createDefaultProject')
192-
.resolves(defaultProject as any);
193-
194215
const useEnvExtensionStub = sandbox.stub(envExtApiInternal, 'useEnvExtension').returns(false);
195216
const getEnvExtApiStub = sandbox.stub(envExtApiInternal, 'getEnvExtApi');
196217

197-
const projects = await (controller as any).discoverWorkspaceProjects(workspaceUri);
218+
const fakeDiscoveryAdapter = { kind: 'discovery' };
219+
const fakeExecutionAdapter = { kind: 'execution' };
220+
sandbox.stub(projectUtils, 'createTestAdapters').returns({
221+
discoveryAdapter: fakeDiscoveryAdapter,
222+
executionAdapter: fakeExecutionAdapter,
223+
} as any);
224+
225+
const interpreterService = {
226+
getActiveInterpreter: sandbox.stub().resolves({
227+
displayName: 'Python 3.11',
228+
path: '/usr/bin/python3',
229+
version: { raw: '3.11.8' },
230+
sysPrefix: '/usr',
231+
}),
232+
} as any;
233+
234+
const configSettings = {
235+
getSettings: sandbox.stub().returns({
236+
testing: { unittestEnabled: false },
237+
}),
238+
} as any;
239+
240+
const testController = createStubTestController();
241+
const envVarsService = {} as any;
242+
243+
const registry = new TestProjectRegistry(
244+
testController,
245+
configSettings,
246+
interpreterService,
247+
envVarsService,
248+
);
249+
250+
const projects = await registry.discoverAndRegisterProjects(workspaceUri);
198251

199252
assert.strictEqual(useEnvExtensionStub.called, true);
200253
assert.strictEqual(getEnvExtApiStub.notCalled, true);
201-
assert.strictEqual(createDefaultProjectStub.calledOnceWithExactly(workspaceUri), true);
202-
assert.deepStrictEqual(projects, [defaultProject]);
254+
assert.strictEqual(projects.length, 1);
255+
assert.strictEqual(projects[0].projectUri.toString(), workspaceUri.toString());
203256
});
204257

205258
test('filters Python projects to workspace and creates adapters for each', async () => {
206-
const controller = createController();
207259
const workspaceUri: Uri = vscodeApi.Uri.file('/workspace/root');
208260

209261
const pythonProjects = [
@@ -215,60 +267,103 @@ suite('PythonTestController', () => {
215267
sandbox.stub(envExtApiInternal, 'useEnvExtension').returns(true);
216268
sandbox.stub(envExtApiInternal, 'getEnvExtApi').resolves({
217269
getPythonProjects: () => pythonProjects,
270+
getEnvironment: sandbox.stub().resolves({
271+
name: 'env',
272+
displayName: 'Python 3.11',
273+
shortDisplayName: 'Python 3.11',
274+
displayPath: '/usr/bin/python3',
275+
version: '3.11.8',
276+
environmentPath: vscodeApi.Uri.file('/usr/bin/python3'),
277+
sysPrefix: '/usr',
278+
execInfo: { run: { executable: '/usr/bin/python3' } },
279+
envId: { id: 'test', managerId: 'test' },
280+
}),
218281
} as any);
219282

220-
const createdAdapters = [
221-
{ projectId: 'p1', projectUri: pythonProjects[0].uri },
222-
{ projectId: 'p2', projectUri: pythonProjects[1].uri },
223-
];
283+
const fakeDiscoveryAdapter = { kind: 'discovery' };
284+
const fakeExecutionAdapter = { kind: 'execution' };
285+
sandbox.stub(projectUtils, 'createTestAdapters').returns({
286+
discoveryAdapter: fakeDiscoveryAdapter,
287+
executionAdapter: fakeExecutionAdapter,
288+
} as any);
224289

225-
const createProjectAdapterStub = sandbox
226-
.stub(controller as any, 'createProjectAdapter')
227-
.onFirstCall()
228-
.resolves(createdAdapters[0] as any)
229-
.onSecondCall()
230-
.resolves(createdAdapters[1] as any);
290+
const interpreterService = {
291+
getActiveInterpreter: sandbox.stub().resolves(null),
292+
} as any;
231293

232-
const createDefaultProjectStub = sandbox.stub(controller as any, 'createDefaultProject');
294+
const configSettings = {
295+
getSettings: sandbox.stub().returns({
296+
testing: { unittestEnabled: false },
297+
}),
298+
} as any;
233299

234-
const projects = await (controller as any).discoverWorkspaceProjects(workspaceUri);
300+
const testController = createStubTestController();
301+
const envVarsService = {} as any;
235302

236-
// Should only create adapters for the 2 projects in the workspace.
237-
assert.strictEqual(createProjectAdapterStub.callCount, 2);
238-
assert.strictEqual(
239-
createProjectAdapterStub.firstCall.args[0].uri.toString(),
240-
pythonProjects[0].uri.toString(),
241-
);
242-
assert.strictEqual(
243-
createProjectAdapterStub.secondCall.args[0].uri.toString(),
244-
pythonProjects[1].uri.toString(),
303+
const registry = new TestProjectRegistry(
304+
testController,
305+
configSettings,
306+
interpreterService,
307+
envVarsService,
245308
);
246309

247-
assert.strictEqual(createDefaultProjectStub.notCalled, true);
248-
assert.deepStrictEqual(projects, createdAdapters);
310+
const projects = await registry.discoverAndRegisterProjects(workspaceUri);
311+
312+
// Should only create adapters for the 2 projects in the workspace (not 'other')
313+
assert.strictEqual(projects.length, 2);
314+
const projectUris = projects.map((p) => p.projectUri.fsPath);
315+
assert.ok(projectUris.includes('/workspace/root/p1'));
316+
assert.ok(projectUris.includes('/workspace/root/nested/p2'));
317+
assert.ok(!projectUris.includes('/other/root/p3'));
249318
});
250319

251320
test('falls back to default project when no projects are in the workspace', async () => {
252-
const controller = createController();
253321
const workspaceUri: Uri = vscodeApi.Uri.file('/workspace/root');
254322

255323
sandbox.stub(envExtApiInternal, 'useEnvExtension').returns(true);
256324
sandbox.stub(envExtApiInternal, 'getEnvExtApi').resolves({
257325
getPythonProjects: () => [{ name: 'other', uri: vscodeApi.Uri.file('/other/root/p3') }],
258326
} as any);
259327

260-
const defaultProject = { projectId: 'default', projectUri: workspaceUri };
261-
const createDefaultProjectStub = sandbox
262-
.stub(controller as any, 'createDefaultProject')
263-
.resolves(defaultProject as any);
328+
const fakeDiscoveryAdapter = { kind: 'discovery' };
329+
const fakeExecutionAdapter = { kind: 'execution' };
330+
sandbox.stub(projectUtils, 'createTestAdapters').returns({
331+
discoveryAdapter: fakeDiscoveryAdapter,
332+
executionAdapter: fakeExecutionAdapter,
333+
} as any);
334+
335+
const interpreter = {
336+
displayName: 'Python 3.11',
337+
path: '/usr/bin/python3',
338+
version: { raw: '3.11.8' },
339+
sysPrefix: '/usr',
340+
};
341+
342+
const interpreterService = {
343+
getActiveInterpreter: sandbox.stub().resolves(interpreter),
344+
} as any;
345+
346+
const configSettings = {
347+
getSettings: sandbox.stub().returns({
348+
testing: { unittestEnabled: false },
349+
}),
350+
} as any;
264351

265-
const createProjectAdapterStub = sandbox.stub(controller as any, 'createProjectAdapter');
352+
const testController = createStubTestController();
353+
const envVarsService = {} as any;
354+
355+
const registry = new TestProjectRegistry(
356+
testController,
357+
configSettings,
358+
interpreterService,
359+
envVarsService,
360+
);
266361

267-
const projects = await (controller as any).discoverWorkspaceProjects(workspaceUri);
362+
const projects = await registry.discoverAndRegisterProjects(workspaceUri);
268363

269-
assert.strictEqual(createProjectAdapterStub.notCalled, true);
270-
assert.strictEqual(createDefaultProjectStub.calledOnceWithExactly(workspaceUri), true);
271-
assert.deepStrictEqual(projects, [defaultProject]);
364+
// Should fall back to default project since no projects are in the workspace
365+
assert.strictEqual(projects.length, 1);
366+
assert.strictEqual(projects[0].projectUri.toString(), workspaceUri.toString());
272367
});
273368
});
274369
});

0 commit comments

Comments
 (0)