Skip to content
This repository was archived by the owner on Jan 16, 2025. It is now read-only.

Mocking not working #14

Closed
swissspidy opened this issue Aug 12, 2021 · 60 comments
Closed

Mocking not working #14

swissspidy opened this issue Aug 12, 2021 · 60 comments

Comments

@swissspidy
Copy link

When using @swc/jest as described in the readme for transforms in Jest tests, I can't seem to use any mocking.

With a test like this:

import got from 'got';

jest.mock('got');

describe('My Test', () => {
  // ...
});

I will get:

TypeError: _got.default.mockImplementationOnce is not a function

Moving the jest.mock() call to the top of the test file also doesn't work.

Any advice appreciated.

@unlight
Copy link

unlight commented Aug 30, 2021

Same.
By design jest.mock() is hoisting over import.
Also other methods should hoist ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock']

https://github.com/kulshekhar/ts-jest/blob/master/src/transformers/hoist-jest.ts

@chyzwar
Copy link

chyzwar commented Oct 15, 2021

for us mocking stopped working once we added "runtime": "automatic"

@gabberr
Copy link

gabberr commented Oct 18, 2021

Our workaround was to declare jest.mock in another file and then import it in the test.

@diego-aquino
Copy link

Our workaround was to declare jest.mock in another file and then import it in the test.

@gabberr, could you provide an example? I've tried it, but I still get the same error.

@nvh95
Copy link

nvh95 commented Nov 16, 2021

Hope this issue will be resolved soon. I try to switch from babel-jest to @swc/jest and this is the only remaining issue has left that blocks me from actually switch.

@gabberr
Copy link

gabberr commented Nov 16, 2021

@diego-aquino here:

Create a file 'src/mocks/bunny.ts':

const mockedBunny = jest.fn()
jest.mock(
  'actual/bunny/file',
  () => mockedBunny
)

export default mockedBunny

Inside the test where you want to mock Bunny:

import 'src/mocks/bunny'

//  OR, if you want to set mock implementation in the test

import bunnyMock from 'mocks/bunny'

bunnyMock.mockImplementation(() => 'my mocked bunny')

@Ayc0
Copy link
Contributor

Ayc0 commented Nov 24, 2021

Try with

{
    "jsc": {
        "transform": {
            "hidden": {
                "jest": true
            }
        }
    }
}

I just did and it seems to be working

See https://github.com/swc-project/swc/blob/2462b9941f94bc475cf9ff9c67e3b7c1f98739cc/node-swc/__tests__/transform/hidden_jest.js

@Ayc0 Ayc0 mentioned this issue Nov 24, 2021
@kdy1 kdy1 closed this as completed in 38e3fcb Nov 24, 2021
@Ayc0
Copy link
Contributor

Ayc0 commented Nov 24, 2021

Update: setting hidden.jest = true is already set by @swc/jest, but it wasn't applied, swc-project/swc#44 fixes that.

At the moment of my message, the latest version of @swc/jest (0.2.9) doesn't include the fix yet, but I think it'll be released in the close future

@th3fallen
Copy link

im actually still running into this, any work around?

@gabberr
Copy link

gabberr commented Dec 15, 2021

@th3fallen have you tried #14 (comment) ?

@kdy1 The mentioned fix that closes this ticket was actually reverted, should this be reopened?

@kdy1 kdy1 reopened this Dec 15, 2021
@Ayc0
Copy link
Contributor

Ayc0 commented Dec 15, 2021

@gabberr it was reverted indeed in swc-jest, but a commit was added to swc itself to fix the issue: see #44 (comment)

Can you try with an up to date swc?

@kdy1
Copy link
Member

kdy1 commented Dec 15, 2021

Ah, I forgot it.
@Ayc0 Thank you!

@Ayc0
Copy link
Contributor

Ayc0 commented Dec 15, 2021

There should be a test for jest's mocking mechanism: #46
So it should work now.

Can you try updating your versions of both swc and swc-jest to see if it is still broken for you? (And also can you send a way to replicate the bug? So that we can add more tests)

@MhMadHamster
Copy link

MhMadHamster commented Dec 15, 2021

There should be a test for jest's mocking mechanism: swc-project/swc#46 So it should work now.

Can you try updating your versions of both swc and swc-jest to see if it is still broken for you? (And also can you send a way to replicate the bug? So that we can add more tests)

It still doesn't work if you want to mock implementation and pass mock function as the desired export for the module that you're trying to mock. See: https://github.com/MhMadHamster/swc-jest-mock-bug/tree/master
You can get around it tho by doing something like this:

import { callFnOne } from "./index";

const mockedImpl = jest.fn();

jest.mock("./to-be-mocked", () => ({
  fnOne: (...args) => mockedImpl(...args),
}));

describe("", () => {
  it("fnOne test", () => {
    callFnOne()
    expect(mockedImpl).toBeCalledTimes(1);
  });
});

@Ayc0
Copy link
Contributor

Ayc0 commented Dec 16, 2021

I forked your repo here: https://github.com/Ayc0/swc-jest-mock-bug if you want to give it a try.

I think this is related to: https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter

image

In the fork, you can see that I disabled swc and here are my results without swc:
image

and here with swc:
image

There seems to be a difference with the hoisting of mock* variables, but your example still failed without SWC.

The mock* could be investigated indeed (what do you think @kdy1?)

@kdy1
Copy link
Member

kdy1 commented Dec 16, 2021

I think the exception with swc should match the exception without swc.
Seems like a different issue though.

@Ayc0
Copy link
Contributor

Ayc0 commented Dec 16, 2021

I agree, I'll open another issue

@Ayc0
Copy link
Contributor

Ayc0 commented Dec 16, 2021

I opened #59 for this issue specifically.

I think we can close this one then

@th3fallen
Copy link

th3fallen commented Dec 16, 2021

in my case the mocking failure is from this...

import { getUserId } from 'shared/auth';

getUserId.mockReturnValue('1');

describe("", () => {
  it("getUserId test", () => {
    
    expect(getUserId).toBe(1);
  });
});

@MhMadHamster
Copy link

I think this is related to: https://jestjs.io/docs/es6-class-mocks#calling-jestmock-with-the-module-factory-parameter

I discovered that i had this issue because I was using ts-jest before, and apparently it allows using local variables in the mock factory, hence i thought issue was in swc 😕

@Ayc0
Copy link
Contributor

Ayc0 commented Dec 17, 2021

I think ts-jest is using tsc which usually changes the variables from const/let to var (and var variables are hoisted to the top of the file).

But this is a guess.

@kdy1
Copy link
Member

kdy1 commented Jan 27, 2022

Is this still an issue?

@kdy1
Copy link
Member

kdy1 commented Feb 22, 2023

import * as foo from 'foo' is the syntax for ESM.

@kdy1
Copy link
Member

kdy1 commented Feb 22, 2023

I know - tsc miscompiles them, but I'm not going to follow the wrong behavior of tsc

@Kampfmoehre
Copy link

I am a little bit confused now.
The test is written in TypeScript using the default import from syntax but when jest runs the test should have been transpiled to CommonJS with a require syntax. When I manually transpile the code it is emitted as such and until now I assumed SWC would do exactly the same before handing the code to jest?
The tests worked for some time until a recent version of swc. Or am I wrong here and SWC is producing ESM code now?

@kdy1
Copy link
Member

kdy1 commented Feb 22, 2023

You used the syntax for ESM, so it should follow semantics of ESM

@pm0u
Copy link

pm0u commented Mar 8, 2023

EDIT: the example in this comment actually passes, see below

I am having trouble overriding an import in a file that is being tested, I'm unsure if this issue is related...

// items/valid-items.ts

export const validItems = ["item1", "item2"]
// item-validator.ts
import { validItems } from './items/valid-items'

export const isValidItem = (item: string) => {
  return validItems.includes(item)
}
// item-validator.test.ts
import { isValidItem } from './item-validator'
import { jest, test, expect } from '@jest/globals'

jest.mock('./items/valid-items', () => ({
    validItems: ["item3", "item4"]
  })
  
test("Items are valid", () => {
  const isValid = isValidItem("item3")
  // test fails
  expect(isValid).toBe(true)
})

edit: I guess it apparently it is, since @gabberr 's fix above worked. thought i was losing my mind.

@Wazbat
Copy link

Wazbat commented Mar 16, 2023

I'm also running into this same issue. I'm trying to use jest.mock('./service'); however no matter where in the file I put the mock call, jest continues to import the original file

@kdy1 kdy1 closed this as not planned Won't fix, can't repro, duplicate, stale Mar 17, 2023
@kdy1 kdy1 reopened this Mar 17, 2023
@kdy1 kdy1 closed this as completed Mar 17, 2023
@Wazbat
Copy link

Wazbat commented Mar 23, 2023

I see the issue is closed, was this fixed? In what version?

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

@Wazbat do you have a reproduction of your error?

@pm0u
Copy link

pm0u commented Mar 23, 2023

@Ayc0 I created this repo:

https://github.com/pm0u/swc-jest-mock-issue

The simple issue I highlighted passes, so excuse that example. I dug a bit deeper and it seems (for my case) to be related to use of Regex.

You can try yarn test-swc and yarn test and see that the CommonJS example passes while the SWC example does not.


edit: I also added an example like @gabberr 's above - you can try that with yarn test-swc-external to see that this does bring in the proper mock and the test passes. I am unsure what the functional difference is between any of these.

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

There seems to indeed be an issue with the mocking with CJS: if we build in ESM, it hoist properly the jest.mock (REPL):

image

But not with CJS (it gets added after the requires) (REPL):
image

But the test works again if we remove the imports from @jest/globals (REPL):

image

We should create another issue as this seems to be only related to @jest/globals

@Smrtnyk
Copy link
Contributor

Smrtnyk commented Mar 23, 2023

There seems to indeed be an issue with the mocking with CJS: if we build in ESM, it hoist properly the jest.mock (REPL):

image

But not with CJS (it gets added after the requires) (REPL): image

But the test works again if we remove the imports from @jest/globals (REPL):

image

We should create another issue as this seems to be only related to @jest/globals

are you sure it is not this swc-project/swc#5205?

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

In your case @pm0u, you can just drop the imports on @jest/globals and it should work great:

  • run yarn add -D @types/jest to have the global types working with TS
  • drop the package @jest/globals & remove the imports

image

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

are you sure it is not this swc-project/swc#5205?

You don't seem to be running ESM code in this example (the source is ESM, but not the compiled code). Also to my knowledge, Jest isn't compatible with mocks with ESM

@Smrtnyk
Copy link
Contributor

Smrtnyk commented Mar 23, 2023

but he is using esm syntax, and swc treats it strictly as esm

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

This issue with @jest/globals seems to be related to swc-project/plugins#310, which indeeds points back to swc-project/swc#5205

@pm0u
Copy link

pm0u commented Mar 23, 2023

The use case I have is this is a script that is ultimately run using node -r @swc-node/register file.ts

I just want to be able to write a test in typescript and have predictable behavior with the environment it is run in, I have no idea what that above command actually compiles to.

Dropping the @jest/globals could work for my use case, but I don't love just having jest as a global in every file in the project as this isn't really true. As a "workaround", I suppose it's acceptable.

@Ayc0
Copy link
Contributor

Ayc0 commented Mar 23, 2023

If you want to keep @jest/globals, you'll have to do:

import { jest, test, expect } from "@jest/globals";

jest.mock("./items", () => ({
  items: ["baz", "qux"],
}));

test("ItemsRegex", async () => {
  const { itemsRegex } = await import("./items-regex");
  expect(itemsRegex).toStrictEqual(/(baz|qux)/g);
});

image

As SWC is respecting the fact that imports are hoisted, this is the other only option to be able to have jest.mock guaranteed to be called before the other imports

@pm0u
Copy link

pm0u commented Mar 23, 2023

I see, thanks for the explanation and solution! this is obviously a complex problem and I appreciate everyone taking the time to sort it out correctly and in line with established standards. I think we will be able to work with the provided solutions. thanks again.

@tangentlin
Copy link

In my case, import from @jest/globals was the culprit, by removing it the tests run fine.

ziedHamdi pushed a commit to ziedHamdi/user-credits that referenced this issue Oct 15, 2023
ziedHamdi pushed a commit to ziedHamdi/user-credits that referenced this issue Oct 15, 2023
@kkirby
Copy link

kkirby commented Feb 20, 2024

I switched to Vitest after I couldn't get jest working.

@leonardotc
Copy link

Just to add. The above solutions worked for me (using the factory). The thing is that the variables in that context retain their original values so if you need to access their "mocked versions" you will need to use jest.requireMock.

jest.mock('./utils', () => ({
  ...jest.requireActual('./utils'),
  createTransport: jest.fn(() => ({ send: handleWithMock }))
}))
it('should do something else', async () => {
      const mockedUtils = jest.requireMock('./utils')
      mockedUtils.createTransport.mockImplementationOnce(() => ({
        send: overridenHandleWithMock
      }))
})

Idk if it will help anyone but it took me a while to figure it out.

@Sammaye
Copy link

Sammaye commented Dec 7, 2024

I am still getting this on the latest version of SWC, removing the ject globals import seemed to do the trick

@Sammaye
Copy link

Sammaye commented Dec 7, 2024

Also simply weirdly editing it to remove only certain imports works i.e.:

import { afterAll, beforeAll, describe, expect, it, test } from '@jest/globals';

Is ok but

import { afterAll, beforeAll, describe, expect, it, jest, test } from '@jest/globals';

Is not

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Development

No branches or pull requests