marchaos / jest-mock-extended

Type safe mocking extensions for Jest https://www.npmjs.com/package/jest-mock-extended
MIT License
839 stars 57 forks source link

`.calledWith` ignores plain objects as its condition #109

Closed Manstrike closed 1 year ago

Manstrike commented 1 year ago

Hi! Firstly, i'd like to thank you for this lib, as it adds functionality, that i missed so hard in vanilla jest (i've been using jasmine for a long time before) So, i've ran into a problem when using calledWith. It appears, calledWith completely ignores plain objects as condition:

class TestDependency {
    doSomething({ value }: { value: string }) {}
}

class ClassToTest {
    private someDependency: TestDependency;    

    constructor (dependency: TestDependency) {
        this.someDependency = dependency;
    }

    testMethod(value: string) {
        return this.someDependency.doSomething({ value });
    }
}

// Specs:
const dependency = mock<TestDependency>();
let classToTest: ClassToTest;

beforeEach(() => {
    dependency.doSomething.calledWith({ value: 'test value' }).mockReturnValue('plain object value tested');

    classToTest = new ClassToTest(dependency);
});

it('should check if dependency method called properly', () => {
     classToTest.testMethod('value');

     expect(dependency.doSomething).toHaveBeenCalledOnceWith({ value: 'value' }); // passes
});

it('should return calculated value', () => {
     expect(classToTest.testMethod('value')).toBe('plain object value tested'); // fails: Received undefined
});

So, it looks like .calledWith().mockReturnValue() is being ignored when 'calledWith` condition is a plain object. Thank you in advance for your time!

tkqubo commented 1 year ago

We can for example introduce this kind of wrapper to match against a plain object

import { isEqual } from 'lodash';

function matches(expected: any, description?: string): Matcher<any> {
  return new Matcher<any>(actual => isEqual(actual, expected), description || `${expected} should be expected`);
}

// ... 
dependency.doSomething.calledWith(matches({ value: 'test value' })).mockReturnValue('plain object value tested');

I guess this works.

Manstrike commented 1 year ago

Thank you so match for reply! It actually works. I'm kinda feel disappointment about the fact it is not a "vanilla" feature, but I think solution is just fine for the first time) With minor refactoring, here is what i ended up with:

const matches: MatcherCreator<object> = (expected, description?: string) => {
    return new Matcher((actual) => isEqual(actual, expected), description || `${expected} should be expected`);
};