swc-node icon indicating copy to clipboard operation
swc-node copied to clipboard

Can't use jest spies for functions in same file

Open bfaulk96 opened this issue 4 years ago • 8 comments

If I have a file that contains

// myFile.ts
export const addOne = (x: number): string => `${x + 1}`
export const someFunc = (x: number): string => `The answer is : ${addOne(x)}`

And I want to test the two functions separately and therefore mock addOne:

import * as myFile from './myFile'

describe('someFunc', () => {
  const expected = '2';
  const addOneSpy = jest.spyOn(myFile, 'addOne').mockResolvedValue(expected);
  
  it('should call addOne', () => {
    expect(addOneSpy).toHaveBeenCalledWith(1);
  });
});

with ts-jest, this code snippet provided works. With @swc-node/jest, it seems any jest spies from the same file cannot be spied on, and if they are, result in the error:

expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: 1
Number of calls: 0

Note that I have tried the exact same scenario but exporting them as actual functions (i.e. export function someFunc(x: number): string {), but still run into the same problem.

bfaulk96 avatar Sep 21 '21 20:09 bfaulk96

I have same pb too. It works perfectly with ts-jest.

tduyng avatar Oct 22 '21 07:10 tduyng

I'll take a look soon

Brooooooklyn avatar Oct 22 '21 07:10 Brooooooklyn

It is because SWC doesn't add exports. prefix function calls in the same file.

SWC :

"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
exports.someFunc = exports.addOne = void 0;
const addOne = (x)=>`${x + 1}`
;
exports.addOne = addOne;
const someFunc = (x)=>`The answer is : ${addOne(x)}`

TypeScript:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.someFunc = exports.addOne = void 0;
const addOne = (x) => `${x + 1}`;
exports.addOne = addOne;
const someFunc = (x) => `The answer is : ${(0, exports.addOne)(x)}`;
exports.someFunc = someFunc;

Brooooooklyn avatar Oct 27 '21 03:10 Brooooooklyn

/cc @kdy1

Brooooooklyn avatar Oct 27 '21 06:10 Brooooooklyn

Seems like https://github.com/swc-project/swc/issues/2549 ?

kdy1 avatar Oct 27 '21 07:10 kdy1

Yes, thanks!

Brooooooklyn avatar Oct 27 '21 07:10 Brooooooklyn

I haven't tried this lib again since this issue, did #2569 fix this, or is this still expected at the moment?

bfaulk96 avatar May 27 '22 17:05 bfaulk96

This still seems to be an error. @Brooooooklyn any idea when this will be addressed, or if ever?

bfaulk96 avatar Jul 05 '22 19:07 bfaulk96

This is not an error/bug. That something else makes something possible does not make it right. SWC is the one doing the right thing here, as the resolved ESM exports are supposed to be immutable. Just for reference, the output for CommonJS has changed a bit to be closer to actual ESM semantics, which should clarify what is going on (it's using getters):

"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
function _export(target, all) {
    for(var name in all)Object.defineProperty(target, name, {
        enumerable: true,
        get: all[name]
    });
}
_export(exports, {
    addOne: function() {
        return addOne;
    },
    someFunc: function() {
        return someFunc;
    }
});
const addOne = (x)=>`${x + 1}`;
const someFunc = (x)=>`The answer is : ${addOne(x)}`;

If you actually intend for addOne to be replaceable, make it explicit and open of for some pure DI: just make it a let and expose a export const _setAddOne(mockImpl){ addOne = mockImpl; }. Done.

fatso83 avatar Aug 26 '23 00:08 fatso83

Thank you! See https://github.com/swc-project/swc/issues/7435

kdy1 avatar Aug 26 '23 00:08 kdy1