I'm using jest to test a mongoDB ObjectID.
In the function I check if the id is valid. Then I return the ObjectId.
In my test I'm expecting to get a string using valueOf() of the ObjectId, but the test is failing:
import { ObjectId } from 'mongodb'
const safeObjectId = (id) => {
return ObjectId.isValid(id) ? new ObjectId(id) : null
}
it('should return ObjectId if it is valid', () => {
const result = safeObjectId('5a1154523a6bcc1d245e143d')
expect(typeof result).toBe('object')
expect(result.valueOf()).toEqual('5a1154523a6bcc1d245e143d')
})
But I do get the error
Expected value to equal:
"5a1154523a6bcc1d245e143d"
Received:
"5a1154523a6bcc1d245e143d"
Difference:
Comparing two different types of values. Expected string but received object.
You need to access the 'str' attribute:
const safeObjectId = (id) => {
return ObjectId.isValid(id) ? new ObjectId(id) : null
}
it('should return ObjectId if it is valid', () => {
const result = safeObjectId('5a1154523a6bcc1d245e143d')
expect(typeof result).toBe('object')
expect(result.str).toEqual('5a1154523a6bcc1d245e143d')
})
From the docs... https://docs.mongodb.com/manual/reference/method/ObjectId/
Access the str attribute of an ObjectId() object, as follows:
ObjectId("507f191e810c19729de860ea").str
p.s. It's bad practice to have multiple assertions in a single test, I would move the object check and value check into two separate tests like so...
const safeObjectId = (id) => {
return ObjectId.isValid(id) ? new ObjectId(id) : null
}
it('should return an object', () => {
const result = safeObjectId('5a1154523a6bcc1d245e143d')
expect(typeof result).toBe('object')
})
it('should return the correct ObjectId', () => {
const result = safeObjectId('5a1154523a6bcc1d245e143d')
expect(result.str).toEqual('5a1154523a6bcc1d245e143d')
})
Related
I have a div with the following html
<div data-cy="pop" style="margin-right:5px;">12,300</div>
I am trying to get 12,3001 convert it to a int and save the value for use in another function. I am getting this error.
cy.then() failed because you are mixing up async and sync code.The value you synchronously returned was: 12300
Here is my code to get the value.
cy.elem('pop').invoke('text').then((num) => {
const res = parseInt(num.replaceAll(',',''))
return res
})
Does anyone know what I'm doing wrong?
It's not clear how you are attempting to pass the value of your parsed int into the another function.
One way to save the value of something is with an alias. To do that you will need to use the .as().
cy.elem('pop')
.invoke('text')
.then((num) => { return parseInt(num.replaceAll(',','')) })
.as('num')
// later down in your code
cy.get('#num')
.then(number => {
functionYouWantToPassYourNumber(number)
})
Bit of an edge-case, TLDR: return cy.wrap(res).
If I run this as a minimal test for your code, it passes
const num = "12,300";
cy.wrap(num).then((numAsString) => {
const numAsInt = parseInt(numAsString.replace(",", ""));
return numAsInt
})
.then(num => {
cy.log(num) // logs 12300 ✅
})
If I add an async line cy.wait(5000) (for example), it fails
const num = "12,300";
cy.wrap(num).then((numAsString) => {
const numAsInt = parseInt(numAsString.replace(",", ""));
cy.wait(5000)
return numAsInt
})
.then(num => {
cy.log(num) ❌
})
If I then cy.wrap() the result, it passes again
const num = "12,300";
cy.wrap(num).then((numAsString) => {
const numAsInt = parseInt(numAsString.replace(",", ""));
cy.wait(5000)
return cy.wrap(numAsInt)
})
.then(num => {
cy.log(num) // logs 12300 ✅
})
Theoretically your code should pass, but if you have another command inside the .then() that could be causing it.
Or possible the cy.elem('pop') is causing it.
For reference, this is Cypress' own test for the error
describe("errors", {defaultCommandTimeout: 100}, () => {
beforeEach(function () {
this.logs = [];
cy.on("log:added", (attrs, log) => {
this.lastLog = log;
this.logs?.push(log);
});
return null;
});
it("throws when mixing up async + sync return values", function (done) {
cy.on("fail", (err) => {
const { lastLog } = this;
assertLogLength(this.logs, 1)
expect(lastLog.get("error")).to.eq(err);
expect(err.message).to.include(
"`cy.then()` failed because you are mixing up async and sync code."
);
done();
});
cy.then(() => {
cy.wait(5000);
return "foo";
});
});
});
I'm testing a method and I need to use a mock object as parameter (I am using typemoq). The problem is that the instanceof check, always return false. How can I solve?
public foo(container: BaseClass): string {
if (container instanceof Application) { // Always returns false when I use a mock of the object
// do something
} else {
// do something else
}
return retval;
}
In my spec.ts
context('foo', () => {
it('should return...', () => {
const container = Mock.ofType<Application>();
const result = class.foo(container.object);
});
});
I tried this too, but the result is the same.
context('foo', () => {
it('should return...', () => {
const application = new Application();
const container = Mock.ofInstance(application);
const result = class.foo(container.object);
});
});
How can I fix this? I need to test what append when the parameter is an instance of 'Application'.
Thank you.
I have found a solution. Just change .object in .target in the tests.
context('foo', () => {
it('should return...', () => {
const application = new Application();
const container = Mock.ofInstance(application);
const result = class.foo(container.target); // instead of .object
});
});
In this way it is possible to mock the methods of 'Application' class and the 'instanceof Class' will return true
Given a function that returns an object promise with a boolean & string:
const test = () => {
return new Promise(async (resolve) => {
resolve({ result: false, error: 'This is an error' });
})
}
I try to destruct these couple of values into constants:
const { result, error } = await test();
However, I always get these Typescript errors:
Property 'result' does not exist on type 'unknown'.ts(2339)
Property 'error' does not exist on type 'unknown'.ts(2339)
I've tried all kinds of combinations, but the only one working is adding the 'any' type, which I believe we should always avoid.
Any idea to define the right types without getting error?
Here you should add the type as the generic parameter to the promise - i.e new Promise<MyType>(...) Example:
type MyPromiseResult = { result: boolean, error: string };
const test = () => {
return new Promise<MyPromiseResult>((resolve) => {
resolve({ result: false, error: 'This is an error' });
})
}
async function doThing() {
const { result, error } = await test();
type ResultType = typeof result; // boolean
type ErrorType = typeof error; // string
}
Give your response a type
return new Promise<{ result: boolean, error: string }>((resolve) => {})
I have a list of constants defined like this
const actions = {}
// Home
actions.HOME = {}
actions.HOME.SET_PROFILE_ID = 'SET_PROFILE_ID'
actions.HOME.LOAD_PROFILE = 'HOME_LOAD_PROFILE'
actions.HOME.SET_PROFILE = 'HOME_SET_PROFILE'
actions.OUTSIDE = {}
actions.OUTSIDE.UPDATE_PROFILE_ID = 'SET_PROFILE_ID' // this should error
module.exports = actions
The objects with in objects is to help intelisense so devs can narrow down as they go.
I want to use jest to write a test that will check to make sure no 2 constants have the same value, no matter the depth, otherwise it can create very odd errors that are hard to debug at run time. I don't really understand the documentation and how I can do this. https://jestjs.io/docs/en/using-matchers But this is my first time making any unit tests.
Thank you
-Edit
This is what I have so far. Based on Jared Smiths comments, I am no where close to the right answer as this is too simple. It only finds the first mistake, not all of them.
describe('Actions.js', () => {
it('verify no duplicate action values', () => {
const flattenActions = []
_.forEach(actions, store => {
_.forEach(store, action => {
flattenActions.push(action)
})
})
const testedActions = []
_.forEach(flattenActions, action => {
expect(testedActions).not.toContain(action)
testedActions.push(action)
})
})
})
First of all you can get all the values of your actions
function getDeepValues(obj) {
let values = [];
for (const key in obj) {
if (typeof obj[key] === 'object') {
const subVals = getDeepValues(obj[key]);
values = [...values, ...subVals];
} else {
values.push(obj[key]);
}
}
return values;
}
Will output something like this:
[ 'SET_PROFILE_ID',
'HOME_LOAD_PROFILE',
'HOME_SET_PROFILE',
'SET_PROFILE_ID' ]
And then you test if the array doesn't contain any duplicates:
function arrayHasNoDuplicate(arr) {
return arr.every(num => arr.indexOf(num) === arr.lastIndexOf(num));
};
Now you have to run your tests:
describe('Actions.js', () => {
it('verify no duplicate action values', () => {
const actionsArray = getDeepValues(actions);
const hasNoDuplicates = arrayHasNoDuplicate(actionsArray);
expect(hasNoDuplicates).toBeTruthy();
})
})
Hope it helps!
A unit test of this complexity probably merits its own matcher, which you could define recursively like this:
expect.extend({
toHaveUniqueValues(received) {
const keys = []
const values = new Set()
function assertUniqueValues(object) {
if (typeof object === 'object' && object !== null) {
for (const key in object) {
if (object.hasOwnProperty(key)) {
keys.push(key)
assertUniqueValues(object[key])
keys.pop()
}
}
} else if (values.has(object)) {
throw new Error(`expected ${keys.join('.')} to not have duplicate value ${String(object)}`)
}
values.add(object)
}
try {
assertUniqueValues(received)
return {
message: () => 'expected object to have duplicate values',
pass: true
}
} catch (error) {
return {
message: () => error.message,
pass: false
}
}
}
})
The message that goes with pass: true, as explained in the documentation, is in case the test is negated and the negation of the test fails. Set is preferred to Array for storing the values found so far, because lookup using has() is O(1) time on average while using includes() is O(n) time.
To use the above matcher in your case:
describe('actions', () => {
it('should not have duplicate values', () => {
expect(actions).toHaveUniqueValues()
})
})
In this case it will complete with the error:
expected OUTSIDE.UPDATE_PROFILE_ID to not have duplicate value SET_PROFILE_ID
Code below is able to parse a specific response from server. In order to make sure the response will be an empty array like output when the serviceResponse param is undefined, I use an default value provided by ES6 features.
I would like to know if you know better way to covering or testing the default function value defined on locationHistoryParser function.
Source
function _getLagLngCoordinates(coordinates) {
...
}
function _getCoordinates(coordinates) {
return coordinates.locations ?
_getLagLngCoordinates(coordinates.locations) :
_getLagLngCoordinates(coordinates);
}
function locationHistoryParser(serviceResponse = '[]') {
return _getCoordinates(JSON.parse(serviceResponse));
}
Test suite
describe('locationHistoryParser', () => {
describe('retrieving a valid response', function () {
describe('managing an array of objects', function () {
it('should return an array of coordinates parsed properly', () => {
...
});
});
describe('managing an object with locations key', function () {
it('should return an array of coordinates parsed properly', () => {
...
});
});
});
describe('retrieving an invalid response', function () {
it('should return an empty array', () => {
const inputData = undefined;
const expectedObject = [];
const response = locationHistoryParser(inputData);
expect(response).toEqual(expectedObject);
});
});
});