Skip to content

Instantly share code, notes, and snippets.

@mayank23
Last active June 30, 2020 22:24
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mayank23/4a5ac7a7cbb745aefc995777519338f5 to your computer and use it in GitHub Desktop.
Save mayank23/4a5ac7a7cbb745aefc995777519338f5 to your computer and use it in GitHub Desktop.
Jest user defined asymmetric matchers
import * as actionTypes from './action-types.js';
class ActionTypeMatching extends AsymmetricMatcher {
constructor(expected){
this.$$typeof = Symbol.for('jest.asymmetricMatcher');
this.expected = expected;
}
asymmetricMatch(other) {
return (other === expected && actionTypes.hasOwnProperty(other))
}
toString() {
return 'ActionTypeMatching';
}
getExpectedType() {
return 'String'
}
toAsymmetricMatcher() {
// to be printed in diff string.
return `ActionTypeMatching(${this.expected})`;
}
}
// patch expect
expect.actionTypeMatching = (expected) => (new ActionTypeMatching(expected));
export const SOME_ACTION = 'SOME_ACTION'
{
"helloWorld" : "hello world"
}
class GreaterThan extends AsymmetricMatcher {
constructor(expected){
this.$$typeof = Symbol.for('jest.asymmetricMatcher');
this.expected = expected;
}
asymmetricMatch(other) {
return other > this.expected;
}
toString() {
return 'GreaterThan';
}
getExpectedType() {
return 'Number'
}
toAsymmetricMatcher() {
// to be printed in diff string.
return `GreaterThan(${this.expected})`;
}
}
// patch expect
expect.greaterThan = (expected) => (new GreaterThan(expected));
// patch expect by importing these files
import from './GreaterThan'
import from './ActionTypeMatching'
import from './TranslationKeyMatching'
describe('custom asymmetric matchers', () => {
describe('numeric limits', () => {
/* a bit contrived but a possible use case */
it('PASS: has x> 9 && y > 19', () => {
const obj = {
x: 10,
y: 20
}
expect(obj).toEqual({
x: expect.greaterThan(9),
y: expect.greaterThan(19)
})
});
it('FAIL: has x>9 && y >19', () => {
const obj = {
x: 9,
y: 19
}
expect(obj).toEqual({
x: expect.greaterThan(9),
y: expect.greaterThan(19)
})
});
});
describe('redux example', () => {
/*
Assume for example, we have a `actionTypes.js` file, which lists all
constants for all the various redux action types in our application.
e.g)
// constants.js
export const SOME_ACTION = 'SOME_ACTION';
...
// end file
Basically, the asymmetric matcher, `actionTypeMatching`,
would test that the action type equals the `expected` value:
'some-action-type' and also exists in our `actionTypes.js` file.
*/
/*
The `someActionCreator` function returns our redux action which is a plain object:
{
type: 'SOME_ACTION',
}
*/
it('PASS: returns valid action type', () => {
const actionObj = someActionCreator();
expect(actionObj).toEqual({
type: expect.actionTypeMatching('SOME_ACTION')
});
});
it('FAIL: returns valid action type', () => {
const actionObj = someActionCreator();
expect(actionObj).toEqual({
type: expect.actionTypeMatching('SOME_ACTION-TYPO')
});
});
});
describe('internationalization (i18n) example', () => {
/*
Assume for example, we have an internationalized application which uses
translation keys for showing the correct message based on the locale.
e.g)
// en-us.json
{
"helloWorld": "hello world"
}
...
// end file
Basically, the asymmetric matcher, `translationKeyMatching`,
would test that the translation key matched the `expected` value:
'helloWorld' and also exists in our `en-us.json` translations file.
(note: simple example, since just checking en-us)
*/
it('PASS: has a valid translation key', () => {
/* let's say we have an object that will have a property that needs to be a translation key*/
const objWithTranslationKey = {
messageId: 'helloWorld',
}
expect(objWithTranslationKey).toEqual({
messageId: expect.translationKeyMatching('helloWorld')
});
});
it('FAIL: has a valid translation key', () => {
/* let's say we have an object that will have a property that needs to be a translation key*/
const objWithTranslationKey = {
messageId: 'helloWorld-typo',
}
expect(objWithTranslationKey).toEqual({
messageId: expect.translationKeyMatching('helloWorld')
});
});
});
});
import translations from './en-us.json';
class TranslationKeyMatching extends AsymmetricMatcher {
constructor(expected){
this.$$typeof = Symbol.for('jest.asymmetricMatcher');
this.expected = expected;
}
asymmetricMatch(other) {
return (other === expected && translations.hasOwnProperty(other))
}
toString() {
return 'TranslationKeyMatching';
}
getExpectedType() {
return 'String'
}
toAsymmetricMatcher() {
// to be printed in diff string.
return `TranslationKey(${this.expected})`;
}
}
// patch expect
expect.translationKeyMatching = (expected) => (new TranslationKeyMatching(expected));
@Mathspy
Copy link

Mathspy commented Aug 13, 2019

Thanks ❤️

@phthhieu
Copy link

We would remove extends AsymmetricMatcher?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment