Skip to content

fix(@schematics/angular): preserve stub-by-default semantics for create Spy with an original function - #34687

Open
MeAkib wants to merge 2 commits into
angular:mainfrom
MeAkib:fix/jasmine-vitest-create-spy-stub-by-default
Open

MeAkib wants to merge 2 commits into
angular:mainfrom
MeAkib:fix/jasmine-vitest-create-spy-stub-by-default

Conversation

@MeAkib

@MeAkib MeAkib commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

jasmine.createSpy(name, originalFn) stores the original function but does not invoke it: the default spy plan is a no-op stub, and the original function only runs after .and.callThrough(). The refactor-jasmine-vitest schematic maps this call to vi.fn(originalFn), which invokes the function immediately, so a migrated bare spy suddenly calls through and can change test behavior.

Append .mockReturnValue(undefined) when createSpy is given an original function and is not chained with an .and strategy. This keeps the original function available for mockReset() while preserving the stub-by-default behavior, mirroring how bare spyOn calls are already handled. Chained spies are unchanged: .and.callThrough() keeps mapping to a plain vi.fn(originalFn) and strategies such as .and.returnValue() keep replacing the stub.

The updated spec fails without the fix: jasmine.createSpy('mySpy', () => 'foo') was migrated to vi.fn(() => 'foo').mockName('mySpy'), which calls the function.

…teSpy with an original function

`jasmine.createSpy(name, originalFn)` stores the original function but does not invoke it:
the default spy plan is a no-op stub, and the original function only runs after
`.and.callThrough()`. The refactor-jasmine-vitest schematic maps this call to
`vi.fn(originalFn)`, which invokes the function immediately, so a migrated bare spy
suddenly calls through and can change test behavior.

Append `.mockReturnValue(undefined)` when `createSpy` is given an original function and is
not chained with an `.and` strategy. This keeps the original function available for
`mockReset()` while preserving the stub-by-default behavior, mirroring how bare `spyOn`
calls are already handled. Chained spies are unchanged: `.and.callThrough()` keeps mapping
to a plain `vi.fn(originalFn)` and strategies such as `.and.returnValue()` keep replacing
the stub.

The updated spec fails without the fix: `jasmine.createSpy('mySpy', () => 'foo')` was
migrated to `vi.fn(() => 'foo').mockName('mySpy')`, which calls the function.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the Jasmine-to-Vitest refactoring transformer to preserve stub-by-default semantics for jasmine.createSpy(name, originalFn). It now appends .mockReturnValue(undefined) when the spy is not chained with .and.callThrough(), preventing the immediate execution of the original function. The feedback suggests expanding test coverage by adding a test case for other chained strategies, such as .and.returnValue(...), to ensure they are also correctly transformed without appending the default stub.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant