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').
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');
})
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.
@abritinthebay I am in exact that situation and this is the first result in Google, maybe this should be reopened?
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.
@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?
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 ;)
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)
Thanks @abritinthebay
So I wrapped that up in an npm module if people want it:
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
https://github.com/jest-community/jest-extended has all the type matchers you could want (I think).
I've been using toBeInstanceOf
in my tests:
expect($wrapper.vm.countries).toBeInstanceOf(Array);
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 ;)
Yeah, and might write your own jest framework, while you're at it.
This one just might top the worst answers you can get on GitHub.
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?
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
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.
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
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)
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.
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);
@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 ;)
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')
})
})
I refactored the implementation provided by @abritinthebay. It seems for me a little bit comfortable to work with.
```javascript
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)`;
}
};
}
});
You should check out my module (linked above). It does a bit more than that. But if that works for you: use it!
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.
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')
})
😞
This is why I suggest my addon above: takes care of this.
InstanceOf is slightly better but prone to similar issues.
Link to it:
thanks for solution @abritinthebay
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
Most helpful comment
A simple toBeType extension for those who want it
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
NOTsetupFiles
(as extend is available only in the former)