Shared with from Codever.
👉 Use the Copy to mine
functionality to copy this snippet to your own personal collection and easy manage
your code snippets.
Use select: false when defining an attribute of a mongoose schema,
like initiator: {type:String, select: false} in the following schema for a bookmark object:
Shared with from Codever.
👉 Use the Copy to mine
functionality to copy this snippet to your own personal collection and easy manage
your code snippets.
Given birthDate and day of death as strings the following method calculates the lifespan in years.
To calculate then the age of the person we call the same calculateLifeSpan method with the day of death set to today
Note - you could as well accept the inputs directly as Date objects
and wouldn’t need then to transform them to dates with new Date() constructor
The following test suite tests the calculateLifeDuration function, which takes two dates as strings and calculates the duration in years between them.
It also uses test.each to run multiple tests with different start and end dates and expected durations.
describe('calculateLifeDuration',()=>{test.each([['2021-01-01','2021-12-31',0],['1980-01-01','2023-03-31',43],['1995-05-15','2023-03-31',27],['2010-10-10','2023-03-31',12],])('calculates duration correctly between %s and %s',(startDate,endDate,expectedDuration)=>{expect(calculateLifeDuration(startDate,endDate)).toEqual(expectedDuration);});});
The next test suite tests the calculateAge function, which takes a birthdate as a string
and calculates the age in years by calling the calculateLifeDuration function with the birthdate and the current date.
To mock today’s date in the calculateAge tests, we can use Jest’s jest.spyOn method
to replace the toISOString method of the Date object with a mocked version that always returns a fixed date.
This way, the calculateAge function will always use the same date when calculating the age, regardless of when the test is run.
describe('calculateAge',()=>{constfixedDate=newDate('2022-03-31T00:00:00.000Z');beforeEach(()=>{jest.spyOn(Date.prototype,'toISOString').mockReturnValue(fixedDate.toISOString());});afterEach(()=>{jest.restoreAllMocks();});test.each([['1990-01-01',32],['1980-05-15',41],['2005-12-31',16],['1995-10-10',26],])('calculates age correctly for birthdate %s',(birthdate,expectedAge)=>{expect(calculateAge(birthdate)).toEqual(expectedAge);});});
In this example, we create a fixedDate object with a fixed date value that we want to use for testing.
In the beforeEach hook, we use jest.spyOn to replace the toISOString method of the Date object with a mock function
that always returns the fixedDate value as an ISO string. This ensures that the calculateAge function always uses the fixedDate
when calculating the age, regardless of when the test is run.
In the afterEach hook, we restore the original toISOString method of the Date object to ensure that other tests are not affected by this mock.
Shared with from Codever.
👉 Use the Copy to mine
functionality to copy this snippet to your own personal collection and easy manage
your code snippets.
Jest is a popular JavaScript testing framework that provides an intuitive and powerful way to write automated tests for your codebase.
One of the features that make Jest stand out is its test.each function,
which enables you to write more concise and readable tests by parameterizing test cases.
With test.each, you can define an array of test cases and run the same test function against each test case
with the input arguments substituted. This feature is particularly useful when you need to test a function
with a large number of input combinations, making your test code more maintainable and less verbose.
In this blog post, you’ll see an example of how I refactored several repetitive tests to use test.each in Jest
and simplify my test code.
When validating a note, bookmark or snippet on Codever several validation rules
might be broken and the response given to the consumer will contain all the corresponding error messages.
In the following example we will consider the case of notes and how we can verify in jest repetitive tests
if a broken rule is present in the error response. To do that we surround the method under test in a trycatch block
and use jest expect.arrayContaining assertion on the validationErrors array in the error content
describe('validateNoteInput',()=>{test.each([// Test cases go here as an array of arrays// Each inner array represents a set of arguments to pass to the function// The last element of the inner array is the expected error message[123,{userId:null,title:'Test',content:'This is a test note'},NoteValidationErrorMessages.MISSING_USER_ID],[456,{userId:789,title:'Test',content:'This is a test note'},NoteValidationErrorMessages.USER_ID_NOT_MATCHING],[111,{userId:111,title:null,content:'This is a test note'},NoteValidationErrorMessages.MISSING_TITLE],[222,{userId:222,title:'Test',content:null},NoteValidationErrorMessages.MISSING_CONTENT],[333,{userId:333,title:'Test',content:'x'.repeat(NoteValidationRules.MAX_NUMBER_OF_CHARS_FOR_CONTENT+1)},NoteValidationErrorMessages.CONTENT_TOO_LONG],])('throws a ValidationError with the correct error message',(userId,note,expectedErrorMessage)=>{try{expect(()=>noteInputValidator.validateNoteInput(userId,note)).toThrowError(ValidationError);expect(()=>noteInputValidator.validateNoteInput(userId,note)).toThrowError(NoteValidationErrorMessages.NOTE_NOT_VALID);}catch(error){// If the function threw an error, test that the error message is correctexpect(error.validationErrors).toEqual(expect.arrayContaining(expectedErrorMessage));}});});
The method under test is defined in the following snippet
constValidationError=require('../../../error/validation.error');letvalidateNoteInput=function(userId,note){letvalidationErrorMessages=[];if(!note.userId){validationErrorMessages.push(NoteValidationErrorMessages.MISSING_USER_ID);}if(note.userId!==userId){validationErrorMessages.push(NoteValidationErrorMessages.MISSING_USER_ID);}if(!note.title){validationErrorMessages.push(NoteValidationErrorMessages.MISSING_TITLE);}if(!note.content){validationErrorMessages.push(NoteValidationErrorMessages.MISSING_CONTENT);}if(note.content){constdescriptionIsTooLong=note.content.length>NoteValidationRules.MAX_NUMBER_OF_CHARS_FOR_CONTENT;if(descriptionIsTooLong){validationErrorMessages.push(NoteValidationErrorMessages.CONTENT_TOO_LONG);}}if(validationErrorMessages.length>0){thrownewValidationError(NoteValidationErrorMessages.NOTE_NOT_VALID,validationErrorMessages);}}constNoteValidationRules={MAX_NUMBER_OF_CHARS_FOR_CONTENT:10000,MAX_NUMBER_OF_TAGS:8}constNoteValidationErrorMessages={NOTE_NOT_VALID:'The note you submitted is not valid',MISSING_USER_ID:'Missing required attribute - userId',USER_ID_NOT_MATCHING:'The userId of the bookmark does not match the userId parameter',MISSING_TITLE:'Missing required attribute - title',MISSING_CONTENT:'Missing required attribute - content',CONTENT_TOO_LONG:`The content is too long. Only ${NoteValidationRules.MAX_NUMBER_OF_CHARS_FOR_CONTENT} allowed`,}module.exports={validateNoteInput:validateNoteInput,NoteValidationRules:NoteValidationRules,NoteValidationErrorMessages:NoteValidationErrorMessages};
Shared with from Codever.
👉 Use the Copy to mine
functionality to copy this snippet to your own personal collection and easy manage
your code snippets.