Jest Custom Matchers: "this" is undefined inside matcher - jestjs

In accordance with the documentation https://jestjs.io/docs/en/expect#custom-matchers-api I create my matcher, however this === undefined inside the matcher function.
I want to use the helpers described in the documentation, such as this.isNot, but they are not available.
What could be the reason for this behavior?
setupJestExpect.ts:
expect.extend({
toBeMoment(received: any): CustomMatcherResult {
console.log(this); // undefined <-- here
const pass: boolean = moment.isMoment(received) as boolean;
return pass
? { pass, message: () => '' }
: { pass, message: () => 'expected that received value to be an instance of Moment' };
}
});
foo.spec.ts:
describe('Foo', () => {
it('should do something', async (): Promise<void> => {
// const data: any[] = ....
expect(data).toEqual(
expect.arrayContaining([
expect.objectContaining({
bar: expect.toBeMoment()
})
])
);
});
});

The matcher is getting undefined in the execution context because of the way you are calling it.
If you call it with:
const m = moment();
expect(m).toBeMoment();
You receive the expected value in this.

Related

Testing promise with stub

I'm trying to do some tests with chai using sinon stub. The thing is, I'm stubbing my fetch like this and resolving my promise.
let fetchedStub;
beforeEach(() => {
fetchedStub = sinon.stub(global, 'fetch');
fetchedStub.resolves({ json: () => { body: 'json' } });
});
Then I'm testing if my data is returning correctly
it('should return the JSON data from the promise', () => {
const result = search('test');
result.then((data) => {
expect(data).to.be.eql({ body: 'json' });
});
});
But instead of passing the test, I'm getting
TypeError: Cannot read property 'then' of undefined
Am I doing something wrong with my promise? I think I need some light here.
Edit: this is the search function.
export const search = (query) => {
fetch(`https://api.spotify.com/v1/search?q=${query}&type=artist`)
.then(data => data.json());
};
Your search arrow function does not return anything, hence in your test result is undefined, hence the error message.
You should simply return the result of your fetch:
export const search = (query) => {
// return something
return fetch(`url`).then(data => data.json());
};
You might have been confused by the arrow function shorthand syntax, which automatically returns the result of a single expression, provided that it is not wrapped in curly braces:
export const search = (query) => fetch(`url`).then(data => data.json()); // no curly braces after the arrow

Mocha/Chai test returns error message and I couldnt find a way to test it

Title says it all and it returns a message like this
Error: startDate is a required field
I tried to use equal, instanceof.
describe('filter', () => {
it('needs to return a startDate required message', async () => {
let dto = {
'endDate': '2000-02-02',
};
let result = await service.filter(dto);
expect(result).to.throw();
};
});
The problem here is you are not testing the error.
Think about that: When you are doing expect(result).to.throw(); the error has been thrown.
Also result didn't throws any error.
So you can test the error is thrown when calling the function.
You can do it using chai as promised in this way:
service.filter(dto).should.be.rejected;
Also, I've testd your approach using this code:
describe('Test', () => {
it('Test1', async () => {
//Only this line pass the test
thisFunctionThrowAnError().should.be.rejected;
//This not pass
let result = await thisFunctionThrowAnError();
expect(result).to.throw();
});
});
async function thisFunctionThrowAnError(){
throw new Error("Can mocha get this error?")
}

How am I suppose to stub a function which is dependent on result of previous function?

I have recently started writing tests and I don't have much experience.If any of the community member could point me in the right direction I would be really thankful. My scenario is simple I am half way through it but unable to solve my exact problem. Below is my code..
return generateServiceToken(req.body.appId, req.body.token, req.auth.userId)
.then(result => {
someService
.createCredentialsForUser(
req.auth.userId,
result.user.uid,
result.user.token
)
.then(result => {
return res.status(201).send(result);
});
})
.catch(error => {
return res.status(500).send({ error: `Credentials not valid - ${error}` });
});
The generateToken function is responsible to call a third party api to generate some credentials for their platform and return us the create credentials.
function generateServiceToken(appId: String, token: String, userId: String) {
return new Promise ((resolve, reject)=>{
const apiURL = `https://someapi.com/api/api.php?op=useradd&token=${token}&addr=${userId}&appid=${appId}`;
request.post(apiURL, (error, response, body) => {
const resp = JSON.parse(body);
if (resp.error) return reject(resp.error);
return resolve(resp);
});
});
}
Whereas, the someService.createCredentialsForUser function is responsible to save those credentials in database and return back the result in simple json format.
I am just stuck in stubbing someService.createCredentialsForUser function while writing the test case for happy-path
My test case is below..
describe.only("controllers/v3/some/", () => {
const c = {};
before(() => {
c.sandbox = sinon.createSandbox();
c.someServiceStub = c.sandbox
.stub(someService, "createCredentialsForUser")
.resolves(VALID_OUTPUT);
});
describe("when the request is valid", () => {
before(() => {
c.agent = setupApp(authenticationMiddleware(USER_ID));
return test(c, VALID_REQUEST_BODY);
});
it("should return 201", () => {
expect(c.response.statusCode).to.equal(201);
});
it("should call createCredentialsForUser", () => {
expect(c.stubs.createCredentialsForUser.called).to.equal(true);
});
});
});
The TestCase function is as follows..
function testCase(context, body = VALID_REQUEST_BODY) {
context.sandbox.resetHistory();
console.log(body.length);
const c = context;
return context.agent
.put(`/v3/some/`)
.send(body)
.then(r => {
c.response = r;
});
//.catch(err=>{c.response=err});
}
My someService.createCredentialsForUser function is responsible to save data into database I want to stub that that in a way that I could expect response return from generateServiceToken
I tried couples of ways which are as follows ..
First, I tried to stub that function in before() but no luck it fails with
error : IllegalArgumentError: init() must be called prior to use.
Second, I tried
c.response = c.sandbox.stub(someService, 'createCredentialsForUser').returns(Promise.resolve(r));
in my test function to stub with the value of resolved promise but no luck in this case it fails with the same error as mentioned above.

jest mocking functions without passing as a callback

I have some code like:
module.exports = {
idCheck: function(errors) {
errors.some( (error) => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
this._recordError('IDCHECK');
}
});
}
};
I am trying to test it using jest with this:
const IDCheck = require(
'./IDCheck'
);
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
jest.spyOn(this, '_recordError');
IDCheck.idCheck(errors);
});
});
however, when running jest, I get
Cannot spy the _recordError property because it is not a function; undefined given instead
Is there a way of mocking, testing for _recordError() having been called, or not called and with the correct parameter, without passing _recordError through as a parameter?
A few things about this line: jest.spyOn(this, '_recordError');
this has to be IDCheck because there is no this in scope since you are using arrow functions that inherit this if previously set (which it isn't). You can console.log(this) right above the line to prove that point.
'_recordError' is not a method of IDCheck. spyOn checks the target's methods, not methods called within it. Now if _recordError is a method of IDCheck, then you should be ok.
Finally, you basically have to return the data you want in order to verify it. There's no real way to check what was passed unless you return it.
Here's a solution I came up with that does not include some fixes you'd have to implement to fix the potential workflow flaws.
const IDCheck = {
idCheck: function(errors) {
return errors.map(error => {
if (error.parentSchema.regexp === '/^((?!\\bMyId\\b).)*$/i') {
return this._recordError('IDCHECK')
}
})
},
_recordError: function(data) {
return data
}
}
let errors = [
{
parentSchema: {
regexp: '/^((?!\\bMyId\\b).)*$/i'
}
}
];
describe('IDCheck', () => {
afterEach(() => {
jest.restoreAllMocks();
});
it('calls _recordError with IDCHECK', () => {
const spy = jest.spyOn(IDCheck, '_recordError')
const check = IDCheck.idCheck(errors).includes('IDCHECK')
expect(spy).toHaveBeenCalled()
expect(check).toBe(true)
});
});

Accessing .mock property of an automocked function

I have this code:
import * as a from 'a-a';
jest.mock('a-a');
describe('a-a', () => {
beforeAll(async () => {
const x = await a.x(1); // Calls the mock
console.log(x); // 1
console.log(a.x.mock) // Undefined
});
});
The mock function is:
export async function x(data) {
cache.push(data);
console.log('HERE'); // this is printed
return data;
}
The mock of the module is in the __mocks__ directory.
The a.x() calls the mocked function, but a.x.mock is undefined.
How is that possible? Where is the .mock property?
So, after some investigation I found out that the functions declared in the __mocks__ directory aren't wrapped by jest.fn() by default.
Personally I find the thing a bit confusing.
So you can do both
function x(data) {
cache.push(data);
return cache;
}
jest.mock('a-a', () => ({x: x}))
if you do everything in the same file, or
jest.mock('a-a');
and then in the __mocks__/a-a.js file
export const x = jest.fn(async (data) => {
cache.push(data);
return cache;
});

Resources