Of all things you can test, the easiest thing is a pure function because they require no setup or teardown. In this exercise, i want to pen down my take on testing a pure function using one of the functions we use in my project isPasswordEnteredValid? 😃.
isPasswordEnteredValid? is a function which takes a string and returns a boolean.
// For simplicity sake RegEx's are edited.
const checkLengthRegex = /(^.{8,}$)/
const checkNonAlphaNumeric = /\W/
const checkUpCaseRegex = /[A-Z]/
const checkLowerCaseRegex = /[a-z]/
const checkIntRegex = /(?=.*[0-9])/
const checkLength = (pwd: string) => checkLengthRegex.test(pwd)
const checkUpCase = (pwd: string) => checkUpCaseRegex.test(pwd)
const checkLowerCase = (pwd: string) => checkLowerCaseRegex.test(pwd)
const checkNonAlphaNumericCase = (pwd: string) => checkNonAlphaNumeric.test(pwd)
const checkInt = (pwd: string) => checkIntRegex.test(pwd)
function isPasswordEnteredValid(password) {
return (
checkLength(password) &&
checkNonAlphaNumeric(password) &&
checkIntRegex(password) &&
checkUpCase(password) &&
checkLowerCase(password)
)
}
For most of the util functions like above general approach is to add a 'test' block for each of the use case.
describe('isPasswordEnteredValid', () => {
test('returns false if password length is < 8', () => {
expect(isPasswordEnteredValid('pra4!')).toBeFalsy()
})
test('returns false if password has no alphabets', () => {
expect(isPasswordEnteredValid('20034059!')).toBeFalsy()
})
test('returns false if password has no numbers ', () => {
expect(isPasswordEnteredValid('Pranava')).toBeFalsy()
})
test('returns false if password has no uppercase letters', () => {
expect(isPasswordEnteredValid('pranava!')).toBeFalsy()
})
test('returns false if password has no lower case letters', () => {
expect(isPasswordEnteredValid('PRANAVA1!')).toBeFalsy()
})
test('returns false if password has no non-alphanumeric characters', () => {
expect(isPasswordEnteredValid('Pranava')).toBeFalsy()
})
test('returns true if the password is strong', () => {
expect(isPasswordEnteredValid('Balugari034059!')).toBeTruthy()
})
})
Another approach is to group the happy path and unhappy path test cases using an array or object.
describe('isPasswordEnteredValid', () => {
const validPasswords = ['Balugari034059!']
const inValidPasswords = [
'pra4!',
'20034059!',
'Pranava',
'pranava!',
'PRANAVA1!',
'Pranava',
]
validPasswords.forEach(password => {
test(`allows ${password} >>`, () => {
expect(isPasswordEnteredValid(password)).toBeTruthy()
})
})
inValidPasswords.forEach(password => {
test(`disallows ${password} >>`, () => {
expect(isPasswordEnteredValid(password)).toBeFalsy()
})
})
})
I tend to use above mentioned conventions to test a javascript functions at my work. Recently, i came across jest-in-case library which provides another convention to test javascript functions.
import cases from 'jest-in-case'
const validPasswordUseCases = {
'valid password': {
password: 'Balugari034059!',
},
}
const invalidPasswordUseCases = {
'< 8 characters': {
password: 'pra4!',
},
'no alphabets': {
password: '20034059!',
},
'no numbers': {
password: 'Pranava!',
},
'no upcase characters': {
password: 'pranava!',
},
'no lowercase characters': {
password: 'PRANAVA1!',
},
'no non-alphanumeric characters': {
password: 'Pranava',
},
}
// cases here provide abstraction to the function
// to be tested.
cases(
'isPasswordEnteredValid: valid passwords',
({pwd}) => {
expect(isPasswordEnteredValid(pwd)).toBeTruthy()
},
validPasswordUseCases,
)
cases(
'isPasswordEnteredValid?: invalid passwords',
({password}) => {
expect(isPasswordEnteredValid(password)).toBe(false)
},
invalidPasswordUseCases,
)
Wrapping up
I hope this article becomes a reference for developers who are looking to write tests for javascript util functions.
I will appreciate the constructive feedback about the article. Please share approaches you have come across in your experience when testing util functions in javascript.
Thanks to Kent C Dodds and Frontendmasters courses :)
Top comments (0)