Jest .toBeCalledWith objectContaining not matching expect on received - unit-testing

I am trying to partially match the object which are being passed to TitleRepo.find. I am not able to understand what wrong I am doing.
expect(jest.fn()).toBeCalledWith(...expected)
Expected: ObjectContaining {"filter": {"limit": Any<Number>}}
Received: {"filter": {"title_ids.xxx.deleted_at": null, "title_ids.xxx.deleted_at": null, "title_ids.xxx.id": {"$exists": true, "$ne": null}}, "limit": 10, "sort": {"created_at": -1}}
Number of calls: 1
31 | });
32 |
> 33 | expect(TitleRepo.find).toBeCalledWith(
| ^
34 | expect.objectContaining({"filter": {"limit": expect.any(Number)}}),
35 | );
36 | })
at Object.<anonymous> (tests/query/TitleQuery.test.js:33:32)
Related lines from test is.
TitleRepo.find = jest.fn();
it('should able to set platform correctly', async () => {
const titles = await TitleQuery.find({
platform: 'chandu'
});
expect(TitleRepo.find).toBeCalledWith(
expect.objectContaining({"filter": {"limit": expect.any(Number)}}),
);
})

limit property is on the same level as filter.
It should be:
expect(TitleRepo.find).toBeCalledWith(expect.objectContaining({ limit: expect.any(Number) })

Related

Trouble switching to urql offlineExchange; runtime error: TypeError: Cannot read property 'length' of undefined

I am attempting to switch to using the urql offlineExchange.
Getting the following error:
<!-- language: lang-none -->
TypeError: Cannot read property 'length' of undefined
ha
https://rdududevw10sdn.dsa.int:3443/static/js/0.chunk.js:23933:10
Module../src/index.js
E:/CCase/sdickerson_wfm14216_view/AnsosWeb/WSM/Application/app/src/index.js:33
30 | optimistic: {},
31 | });
32 |
> 33 | const client = createClient({
34 | url: WSM_URL,
35 | exchanges: [dedupExchange, cache, fetchExchange],
36 | })
I'm attempting to follow the Offline Support documentation (Offline Support.
index.js:
const WSM_URL = '/api/gql'
const introspectedSchema = {
__schema: {
queryType: {name: 'Query',},
mutationType: {name: 'Mutation',},
subscriptionType: {name: 'Subscription',},
},
}
const storage = makeDefaultStorage({
idbName: 'graphcache-v3', // The name of the IndexedDB database
maxAge: 7, // The maximum age of the persisted data in days
})
const cache = offlineExchange({
schema: introspectedSchema,
storage,
updates: {},
optimistic: {},
});
const client = createClient({
url: WSM_URL,
exchanges: [dedupExchange, cache, fetchExchange],
})

Jest mocking document.referrer

I have the following method in a react app service which requires Unit Testing.
onDeclineCallback = () => {
console.log("boom " + document.referrer);
if (document.referrer === "") {
console.log("redirect to our age policy page");
} else {
history.back();
}
};
My unit test currently looks like:
history.back = jest.fn(); // Mocking history.back as jest fn
describe('age verifiction service test', () => {
it('returns user to referrer if declined and referrer is available', () => {
document = {
...document,
referrer: 'Refferer Test', // My hacky attempt at mocking the entire document object
};
ageVerification.onDeclineCallback();
expect(history.back).toHaveBeenCalledTimes(1);
});
});
I am trying to find a way of mocking document.referrer in order to write a unit test for each case. Can anyone provide an approach for this?
You can use Object.defineProperty method to set a mocked value of document.referrer.
E.g.
index.ts:
export class AgeVerification {
public onDeclineCallback = () => {
console.log('boom ' + document.referrer);
if (document.referrer === '') {
console.log('redirect to our age policy page');
} else {
history.back();
}
};
}
index.spec.ts:
import { AgeVerification } from './';
describe('age verifiction service test', () => {
let ageVerification;
beforeEach(() => {
ageVerification = new AgeVerification();
history.back = jest.fn();
});
afterAll(() => {
jest.restoreAllMocks();
jest.resetAllMocks();
});
it('returns user to referrer if declined and referrer is available', () => {
const originalReferrer = document.referrer;
Object.defineProperty(document, 'referrer', { value: 'Refferer Test', configurable: true });
ageVerification.onDeclineCallback();
expect(history.back).toHaveBeenCalledTimes(1);
Object.defineProperty(document, 'referrer', { value: originalReferrer });
});
it('should print log', () => {
const logSpy = jest.spyOn(console, 'log');
ageVerification.onDeclineCallback();
expect(logSpy.mock.calls[0]).toEqual(['boom ']);
expect(logSpy.mock.calls[1]).toEqual(['redirect to our age policy page']);
});
});
Unit test result with 100% coverage:
PASS src/stackoverflow/59198002/index.test.ts (13.915s)
age verifiction service test
✓ returns user to referrer if declined and referrer is available (17ms)
✓ should print log (3ms)
console.log src/stackoverflow/59198002/index.ts:264
boom Refferer Test
console.log node_modules/jest-mock/build/index.js:860
boom
console.log node_modules/jest-mock/build/index.js:860
redirect to our age policy page
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 15.385s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59198002
worked for me, the jest mock way:
jest.spyOn(document, 'referrer', 'get').mockReturnValue('mock ref value');

Using expect.any() with supertest to check response body

I'm trying to use supertest to check res.body with Jest, but the following snippet will always fail
request(app)
.post('/auth/signup')
.send(validEmailSample)
.expect(200, {
success: true,
message: 'registration success',
token: expect.any(String),
user: expect.any(Object),
});
But when I rewrite the test to check the body in a callback as follows:
test('valid userData + valid email will result in registration sucess(200) with message object.', (done) => {
request(app)
.post('/auth/signup')
.send(validEmailSample)
.expect(200)
.end((err, res) => {
if (err) done(err);
expect(res.body.success).toEqual(true);
expect(res.body.message).toEqual('registration successful');
expect(res.body.token).toEqual(expect.any(String));
expect(res.body.user).toEqual(expect.any(Object));
expect.assertions(4);
done();
});
});
The test will pass.
I'm sure it has something to do with expect.any(). As Jest's documentation says that expect.any and expect.anything can only be used together with expect().toEqual, and expect().toHaveBeenCalledWith()
I'm wondering if there's any better way to do it, to use expect.any in supertest's expect api.
You can use expect.objectContaining(object).
matches any received object that recursively matches the expected properties. That is, the expected object is a subset of the received object. Therefore, it matches a received object which contains properties that are present in the expected object.
app.js:
const express = require("express");
const app = express();
app.post("/auth/signup", (req, res) => {
const data = {
success: true,
message: "registration success",
token: "123",
user: {},
};
res.json(data);
});
module.exports = app;
app.test.js:
const app = require('./app');
const request = require('supertest');
describe('47865190', () => {
it('should pass', (done) => {
expect.assertions(1);
request(app)
.post('/auth/signup')
.expect(200)
.end((err, res) => {
if (err) return done(err);
expect(res.body).toEqual(
expect.objectContaining({
success: true,
message: 'registration success',
token: expect.any(String),
user: expect.any(Object),
}),
);
done();
});
});
});
Integration test result with coverage report:
PASS src/stackoverflow/47865190/app.test.js (12.857s)
47865190
✓ should pass (48ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
app.js | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 14.319s
Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/47865190

Test Action of Redux-thunk use JEST

I want to write a test for Axios use Jest Framework. I'm using Redux.
Here is my function get-request of Axios
export const getRequest = a => dispatch => {
return axios
.get(a)
.then(function(response) {
dispatch({
type: FETCH_DATA,
payload: response.data
});
})
.catch(function(error) {
dispatch({ type: ERROR_DATA, payload: { status: error.response.status, statusText: error.response.statusText } });
});
};
thanks in advance :)
Here is the solution:
index.ts:
import axios from 'axios';
export const FETCH_DATA = 'FETCH_DATA';
export const ERROR_DATA = 'ERROR_DATA';
export const getRequest = a => dispatch => {
return axios
.get(a)
.then(response => {
dispatch({
type: FETCH_DATA,
payload: response.data
});
})
.catch(error => {
dispatch({ type: ERROR_DATA, payload: { status: error.response.status, statusText: error.response.statusText } });
});
};
index.spec.ts:
import axios from 'axios';
import { getRequest, FETCH_DATA, ERROR_DATA } from './';
describe('getRequest', () => {
const dispatch = jest.fn();
it('should get data and dispatch action correctly', async () => {
const axiosGetSpyOn = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: 'mocked data' });
await getRequest('jest')(dispatch);
expect(axiosGetSpyOn).toBeCalledWith('jest');
expect(dispatch).toBeCalledWith({ type: FETCH_DATA, payload: 'mocked data' });
axiosGetSpyOn.mockRestore();
});
it('should dispatch error', async () => {
const error = {
response: {
status: 400,
statusText: 'client error'
}
};
const axiosGetSpyOn = jest.spyOn(axios, 'get').mockRejectedValueOnce(error);
await getRequest('ts')(dispatch);
expect(axiosGetSpyOn).toBeCalledWith('ts');
expect(dispatch).toBeCalledWith({ type: ERROR_DATA, payload: error.response });
axiosGetSpyOn.mockRestore();
});
});
Unit test result and coverage:
PASS 45062447/index.spec.ts
getRequest
✓ should get data and dispatch action correctly (9ms)
✓ should dispatch error (2ms)
----------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
index.ts | 100 | 100 | 100 | 100 | |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 1.551s, estimated 3s
Here is the code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/45062447

jasmine parameterized unit test

Okay as a C# NUnit guy this might be odd.
But does jasmine allow parameterized unit test?
I am not sure if it goes against the "declare" and "it" to make things readable to non programmers.
I have seen some third party plug ins but they are kind of old, not sure if it has been added to jasmine.
If I am ment to use a plug in
Just to help anyone who finds this in the future, I have been told on jasmine forum There is no first class support for parameterized tests within Jasmine itself.
Based on piotrek's answer and the article Parameterized testing in Javascript, you could also use the following approach which uses ES6 syntax:
[
['abc', 3],
['ab', 2],
['', 0],
].forEach(([string, expectedLength]) => {
it(`should return length ${expectedLength} for string "${string}"`, () => {
expect(string.length).toBe(expectedLength);
});
});
I have tested it with the Jest test framework, but it should work with Jasmine as well.
Better solution (especially if you use TypeScript)
Another solution is to use Array of Objects instead of Array of Arrays. It fits better if you use some typing system like TypeScript.
Type issue
Imagine you have the following parametrised test:
it('action(value) should reset the forms pool only if value is true', () => {
[
[true, 1],
[false, 0],
].forEach(([value, calledTimes]) => {
spyResetFormsPool.calls.reset();
component.action(value); // type error #1
expect(spyResetFormsPool).toHaveBeenCalledTimes(calledTimes); // type error #2
});
});
with TypeScript, it will fail to compile giving two errors:
error #1:
error TS2345: Argument of type 'number | boolean' is not assignable to parameter of type 'boolean'.
error #2:
error TS2345: Argument of type 'number | boolean' is not assignable to parameter of type 'number'. Type 'true' is not
assignable to type 'number'.
That is because TypeScript sees an array of 'number | boolean'.
We could quickly solve this warning by using some explicit cast:
it('action(value) should reset the forms pool only if value is true', () => {
[
[true, 1],
[false, 0],
].forEach(([value, calledTimes]) => {
spyResetFormsPool.calls.reset();
component.action(value as boolean); // necessary cast
expect(spyResetFormsPool).toHaveBeenCalledTimes(calledTimes as number); // necessary cast
});
});
however this solution is not very nice.
Solution
A better way is to use Array of Objects, so the types are correctly handled by default and there is no need of explicit casting:
it('action(value) should reset the forms pool only if value is true', () => {
[
{ value: true, calledTimes: 1 },
{ value: false, calledTimes: 0 },
].forEach(({ value, calledTimes }) => {
spyResetFormsPool.calls.reset();
component.action(value);
expect(spyResetFormsPool).toHaveBeenCalledTimes(calledTimes);
});
});
Do you want to use for instead of forEach (I personally find it more readable)? That's also possible:
it('action(value) should reset the forms pool only if value is true', () => {
for (const {value, calledTimes} of [
{value: true, calledTimes: 1},
{value: false, calledTimes: 0},
]) {
spyResetFormsPool.calls.reset();
component.action(value);
expect(spyResetFormsPool).toHaveBeenCalledTimes(calledTimes);
}
});
Alternatively, you can also move the it inside the loop. When I do this, I usually add a testId to each object so I can keep track of which tests are failing:
for (const {value, calledTimes} of [
{ testId: 1, value: true, calledTimes: 1 },
{ testId: 2, value: false, calledTimes: 0 },
]) {
it(`action(value) should reset the forms pool only if value is true [${testId}]`, () => {
spyResetFormsPool.calls.reset();
component.action(value);
expect(spyResetFormsPool).toHaveBeenCalledTimes(calledTimes);
});
}
You can use the following convention to increase readability:
const testCases = [
{actualValue: true, expectedValue: true},
{actualValue: false, expectedValue: false}
]
testCases.forEach(({actualValue, expectedValue}) => {
it(`should be the same given: ${actualValue} and expected :${expectedValue} values`, () => {
expect(actualValue).toBe(expectedValue)
})
})
You'll see the following test cases to run:
Test Results
+ should be the same given: true and expected: true values
+ should be the same given: false and expected: false values
i haven't worked with jasmine since a long time but it was pretty easy to add parameterized tests:
['abc', 3,
'ab', 4,
'', 0].
it('should contain string length', function(string, expected){
expect(string.length).toBe(expected);
});
with just a few lines of infrastructure code:
Array.prototype.it = function(description, testCaseFunction) {
_(this)
.chunk(testCaseFunction.length)
.each(function(innerArray){
it(description + ' ' + JSON.stringify(innerArray), function(){
testCaseFunction.apply(this, innerArray);
});
})
.value();
};
depending on your desired syntax and willingness to change default js objects, you have plenty of options: http://blog.piotrturski.net/2015/04/jasmine-parameterized-tests.html
So I started combining:
YAML using js-yaml
jasminejs
Typescript
to create what I believe are readable parameterized tests like this:
import YamlTableReader, {fixtureData, TestData} from "./YamlTableReader";
describe("TestSuite", () => {
describe("Real TestCase with Data Fixture", () => {
// now using tagged template-string to auto convert into YamlTableReader.
var testdata = fixtureData `
| ID | Value1 | Value2 | Squared |
| 0 |1 | 1 | 1 |
| 1 |2 | 2 | 4 |
| 2 |3 | 3 | 91 |
`;
// This actually creates a test for each row of the table above
testdata.describeEach("Square Test","[ID={ID}]:{Value1} x {Value2} should be equal to {Squared}",
(row: {Value1: number, Value2: number, Squared: number}) => {
expect((row.Value1 * row.Value2)).toBe(row.Squared)
}
);
});
Running this will give the following results:
Failures:
1) TestSuite 2 Real TestCase with Data Fixture Square Test : [ID=2]:3 x 3 should be equal to 91
Message:
Expected 9 to be 91.
Sources: https://github.com/deicongmbh/jasmine-param-tests