Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test if object, array or string. #3457

Closed
nithrm opened this issue May 3, 2017 · 32 comments
Closed

Test if object, array or string. #3457

nithrm opened this issue May 3, 2017 · 32 comments

Comments

@nithrm
Copy link

nithrm commented May 3, 2017

Is there a way to check if a component is an object, array or string? This would be similar to chai's 'should.be.a' Ex.: validationResult.SSN[0].should.be.a('string').

@thymikee
Copy link
Collaborator

thymikee commented May 3, 2017

No, there isn't. You'll find the list of all available matchers here: https://facebook.github.io/jest/docs/en/expect.html

You can also use plain JavaScript or helper library like lodash for that:

test('name', () => {
  // array
  expect(Array.isArray(['value'])).toBe(true);
  // string
  expect(typeof 'value').toBe('string');
  // object
  expect({value: 'value'}).toBeTruthy();
  expect(typeof {value: 'value'}).toBe('object');
})

@thymikee thymikee closed this as completed May 3, 2017
@abritinthebay
Copy link

Minor point - this doesn't help with promise results.

expect(somePromise).resolves.toBe(...) at this point there is no way to check type. If you don't care what the contents are but just that it is a string. I hoped expects.stringContaining("") to be a work around but that doesn't work either.

@franciscop
Copy link

@abritinthebay I am in exact that situation and this is the first result in Google, maybe this should be reopened?

@abritinthebay
Copy link

Certainly it should be thought about a bit more. My workaround was to add to the chain so that does the typeof part. eg:

expect(somePromise.then(data => typeof data)).resolves.toBe("object");

it works, but it's not exactly clean.

@abramz
Copy link

abramz commented Oct 5, 2017

@thymikee Checking types of things is a common enough use case (universal) that there isn't really any excuse for a testing framework that lacks them. Your alternatives are unacceptable as we lose all context of what we are testing.

This expect(Array.isArray(['value'])).toBe(false); fails with

expect(received).toBe(expected)
    Expected value to be (using ===):
      false
    Received:
      true. 

So we either get terrible assertion messages or we have to extend Jest to support these sorts of checks. Doesn't it make more sense for the maintainers of Jest to do this once as opposed to every person that uses requires these features implementing them on their own?

@thymikee
Copy link
Collaborator

thymikee commented Oct 6, 2017

Create your own matchers with expect.extend then and publish as an npm module. If it gets popular, we may merge it to Jest core eventually ;)

@abritinthebay
Copy link

abritinthebay commented Oct 6, 2017

A simple toBeType extension for those who want it


expect.extend({
	toBeType(received, argument) {
		const initialType = typeof received;
		const type = initialType === "object" ? Array.isArray(received) ? "array" : initialType : initialType;
		return type === argument ? {
			message: () => `expected ${received} to be type ${argument}`,
			pass: true
		} : {
			message: () => `expected ${received} to be type ${argument}`,
			pass: false
		};
	}
});

describe("testing extended expect", () => {
	it("tests normal types correctly", () => {
		expect("").toBeType("string");
		expect({}).toBeType("object");
		expect(1).toBeType("number");
	});
	it("tests array types correctly", () => {
		expect([]).toBeType("array");
	});
	it("works with promises", () => {
		expect(Promise.resolve([])).resolves.toBeType("array");
	});
});

Pretty simple to implement. Really should be in core tbh.

Note - if you're putting that extend in your setup files then you want it in setupTestFrameworkScriptFile NOT setupFiles (as extend is available only in the former)

@abramz
Copy link

abramz commented Oct 6, 2017

Thanks @abritinthebay

@abritinthebay
Copy link

So I wrapped that up in an npm module if people want it:

https://www.npmjs.com/package/jest-tobetype

@alex-dixon
Copy link

describe("assertion framework", ()=> {
 it("should check primitive types", () => {
   expect(expect.toBeA).toBeA("function")
  })
})

Failed: expect(...).toBeA is not a function
TypeError: expect(...).toBeA is not a function

devenbansod added a commit to devenbansod/babel that referenced this issue Mar 11, 2018
* Add a useful jest extension to compare types
* As posted at: jestjs/jest#3457 (comment)
devenbansod added a commit to devenbansod/babel that referenced this issue Mar 11, 2018
* Add a useful jest extension to compare types
* As posted at: jestjs/jest#3457 (comment)
devenbansod added a commit to devenbansod/babel that referenced this issue Mar 28, 2018
* Add a useful jest extension to compare types
* As posted at: jestjs/jest#3457 (comment)
@SimenB
Copy link
Member

SimenB commented Mar 29, 2018

https://github.com/jest-community/jest-extended has all the type matchers you could want (I think).

@ecoker
Copy link

ecoker commented Aug 13, 2018

I've been using toBeInstanceOf in my tests:

expect($wrapper.vm.countries).toBeInstanceOf(Array);

@vitaly-t

This comment has been minimized.

@machineghost
Copy link

So @abritinthebay did exactly what was requested by @thymikee (which was far more than the standard pull request).

Now that that brave soul did all the work, when can the rest of us finally get this matcher (without having to install yet another library)? Are the maintainers still pushing the idea that this doesn't belong in Jest, or did this just fall off their radar?

@rickhanlonii
Copy link
Member

We are pretty strict with what makes it into core and don't typically add sugar matchers. Jest core is a fairly large architecture and every matcher we add increases the maintenance cost

For sugar, we generally recommend https://github.com/jest-community/jest-extended

@machineghost
Copy link

machineghost commented Dec 2, 2018

One man's sugar is another man's (or in this case, at least seven other people's) really useful and logical feature that belongs in the core library.

Obviously as a maintainer your vote trumps all of ours, and you have all sorts of concerns we don't so I fully respect that. But I'd simply ask that you look at why everyone here considers this feature to belong in the core library (so strongly that one person jumped through multiple hoops to write the code for you). There's a need here, and if you ignore it Jest core library users (and let's be honest, 90% of them will never even hear about jest-extended) will lose out.

.to.be.an.instanceOf is not going to be how many users think to check types, so for those users, even if you see it as sugar, you are effectively denying them the ability to check types in Jest without an additional library.

@rickhanlonii
Copy link
Member

Yeah I hear ya. To be clear, by "sugar" I meant syntax that is designed to make things easier to read or express. Sugar, by definition, is a variation of a feature that already exists

In this case, we have:

// Supported
expect(typeof foo).toBe('string');

// Proposed Sugar
expect(foo).toBeType('string');

So it's not that we don't support checking types. We do. We support the first option. This option uses the core toBe matcher which we have spent a lot of time fixing the bugs in, and tweaking the message for, so that users have a good experience

There are nearly 60 matchers in jest-extended and many of those are pure sugar. For any of those matchers you could probably find at least 7 other people who find them really useful, so if that was the heuristic we used for adding to core we would probably spend all of our time just maintaining matchers

@abritinthebay
Copy link

To be completely fair - most matchers are "sugar" at some level. I mean toBeGreaterThanOrEqual is just sugar for expect(foo >= bar).toBe(true);

Matchers are really almost all sugar around boolean statements ;)

(I say this not to dig, just to point out that it's... a very blurred line)

@machineghost
Copy link

machineghost commented Dec 9, 2018

As abritinthebay suggested, it's not really about sugar, it's about "necessary" and "unnecessary" (for the core library) sugar. You've got a bunch of people in this thread saying "hey, being able to check all types is something that should be in the core of a testing library" (ie. it is necessary).

Listen to us or don't, again as maintainer you have lots of other concerns. But I don't think the right response is to come say "your's is just inherently unnecessary sugar" (that's me trying to paraphrase you, not trying to put words in your mouth) when it's not inherent: it's 100% your call whether Jest can check all types or not out of the box.

@nahumzs
Copy link

nahumzs commented Jan 18, 2019

what about, isn't that hard :P?

expect(Array.isArray(['your', 'array'])).toBe(true);

expect(typeof something === "object").toBe(true); 
// - or -
expect(something instanceof Object).toBe(true);

expect(typeof something === "string").toBe(true); 

@tdmalone
Copy link

@nahumzs While it works, the problem is that on your test output when failing, it will say ‘expected false to be true’, which isn’t very helpful ;)

@amiteshore
Copy link

I think this is the way to go :)

describe('type check', () => {
    test('should be type string', () => {
        expect(typeof '').toBe('string')
    })

    test('should be type number', () => {
        expect(typeof 10).toBe('number')
    })

    test('should be type boolean', () => {
        expect(typeof true).toBe('boolean')
    })

    test('should be type undefined', () => {
        expect(typeof undefined).toBe('undefined')
    })

    test('should be type object', () => {
        expect(typeof { foo: 'bar' }).toBe('object')
    })

    test('should be type function', () => {
        expect(typeof function() {}).toBe('function')
    })

    test('should be type null', () => {
        expect(typeof null).toBe('object')
    })
})

@balovbohdan
Copy link

balovbohdan commented Aug 29, 2019

I refactored the implementation provided by @abritinthebay. It seems for me a little bit comfortable to work with.

expect.extend({
    /**
     * @param {*} received
     * @param {string|string[]} arg
     * @return {{pass:boolean,message:(function():string)}}
     */
    toBeType(received, arg) {
        const isCorrectType = arg => {
            const receivedType = typeof received;

            const checkForSingle = arg => {
                const type = receivedType === 'object'
                    ? Array.isArray(received)
                        ? 'array'
                        : receivedType
                    : receivedType;

                return type === arg;
            };

            const checkForArr = arg => {
                const reducer = (prev, curr) => prev || isCorrectType(curr).isCorrect;

                return arg.reduce(reducer, false);
            };

            return {
                receivedType,
                isCorrect: Array.isArray(arg)
                    ? checkForArr(arg)
                    : checkForSingle(arg)
            };
        };

        const {isCorrect, receivedType} = isCorrectType(arg);

        return {
            pass: isCorrect,
            message: () => {
                const toBe = Array.isArray(arg)
                    ? arg.join(`' or '`)
                    : arg;

                return `Expected '${received}' of '${receivedType}' type to be of '${toBe}' type(s)`;
            }
        };
    }
});

@abritinthebay
Copy link

You should check out my module (linked above). It does a bit more than that. But if that works for you: use it!

@ahtashamabbasse
Copy link

ahtashamabbasse commented Feb 26, 2020

I think this is the way to go :)

describe('type check', () => {
    test('should be type string', () => {
        expect(typeof '').toBe('string')
    })

    test('should be type number', () => {
        expect(typeof 10).toBe('number')
    })

    test('should be type boolean', () => {
        expect(typeof true).toBe('boolean')
    })

    test('should be type undefined', () => {
        expect(typeof undefined).toBe('undefined')
    })

    test('should be type object', () => {
        expect(typeof { foo: 'bar' }).toBe('object')
    })

    test('should be type function', () => {
        expect(typeof function() {}).toBe('function')
    })

    test('should be type null', () => {
        expect(typeof null).toBe('object')
    })
})

It works like a charm as well as more readable and maintainable for the future.

@Billy-
Copy link
Contributor

Billy- commented Mar 19, 2020

I think this is the way to go :)

describe('type check', () => {
    test('should be type string', () => {
        expect(typeof '').toBe('string')
    })

    test('should be type number', () => {
        expect(typeof 10).toBe('number')
    })

    test('should be type boolean', () => {
        expect(typeof true).toBe('boolean')
    })

    test('should be type undefined', () => {
        expect(typeof undefined).toBe('undefined')
    })

    test('should be type object', () => {
        expect(typeof { foo: 'bar' }).toBe('object')
    })

    test('should be type function', () => {
        expect(typeof function() {}).toBe('function')
    })

    test('should be type null', () => {
        expect(typeof null).toBe('object')
    })
})
    test('should be type object', () => {
        expect(typeof { foo: 'bar' }).toBe('object')
        // passes
        expect(typeof ['foo', 'bar']).toBe('object')
        // passes
        expect(typeof null).toBe('object')
    })

😞

@abritinthebay
Copy link

abritinthebay commented Mar 20, 2020

This is why I suggest my addon above: takes care of this.

InstanceOf is slightly better but prone to similar issues.

Link to it:

https://www.npmjs.com/package/jest-tobetype

@Temirtator
Copy link

thanks for solution @abritinthebay

@tfoxy
Copy link

tfoxy commented Nov 9, 2020

Another solution:

expect('example').toEqual(expect.any(String));
expect(123).toEqual(expect.any(String));

The second one would fail with:

Expected: Any<String>
Received: 123

EDIT: keep in mind that null is considered an Object, in which case toMatchObject can be used:

expect(null).toEqual(expect.any(Object)); // passes
expect(null).toMatchObject({}); // fails

In the case of array:

expect([]).toEqual(expect.any(Array)); // passes
expect([]).toEqual(expect.any(Object)); // passes
expect([]).toMatchObject({}); // passes
expect({}).toEqual(expect.any(Array)); // fails

@slavafomin
Copy link

It's extremely weird that Jest doesn't provide so basic functionality out of the box. It's a nonsense that you need to install a third-party library just to check the type of a variable in a clear and semantic manner.

@MentalGear
Copy link

MentalGear commented May 10, 2021

Agreed, a proper type assessor in the core is long overdue - exemplified by the constant infall from people into this issue. typeof as suggested by @rickhanlonii simply tests wrongly for what devs expect (passes null and array types). Disappointing that jest has been going so long without a proper type assessor in the core.. can only fathom how many devs used typeof and now have unknowingly faulty tests due to this unresolved issue
(if reliable testability is jests goal it should ensure that it also covers the human DX side. Providing devs with an easily comprehensible API toolset that covers all the basic is simply integral to any core test suite.)

@github-actions
Copy link

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 10, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests