I'm trying to practice working with javascript closures. But I have a doubt, if we look at my example we see that the methods checkEquipment, checkGender, checkCategory, checkPractice, filterExercises cannot be tested with unit tests.
Do you think it's a good idea to work with closures knowing that we can only test the final result and not all private internals functions ?
JS Closure
import exDb from "../../exercises-database/exercises-database.json"
import {
DatabaseEquipment,
DatabaseExercise,
DatabaseExerciseCategories,
Genders,
Practice,
WKGArgs,
WKGExercises,
} from "../../types"
export function getUserExercises(wkgArgs: WKGArgs): WKGExercises {
const { practice, gender, nonAvailableEquipment } = wkgArgs
const exercisesDatabase = exDb as unknown as DatabaseExercise[]
function checkEquipment(exEquipments: DatabaseEquipment[]): boolean {
return (
exEquipments?.filter(
(exEquipment) => !nonAvailableEquipment.includes(exEquipment)
).length > 0
)
}
function checkGender(exGender: Genders): boolean {
return exGender === gender || exGender === "all"
}
function checkCategory(
exCategories: string[],
category: DatabaseExerciseCategories
): boolean {
return exCategories.includes(category)
}
function checkPractice(exPractice: Practice): boolean {
return exPractice === practice || exPractice === "all"
}
function filterExercises(
category: DatabaseExerciseCategories
): DatabaseExercise[] {
return exercisesDatabase.filter(
(exercise) =>
checkEquipment(exercise.exercise_equipments) &&
checkGender(exercise.gender) &&
checkPractice(exercise.practice) &&
checkCategory(exercise.exercise_categories, category)
)
}
return {
stretching: filterExercises("étirement"),
jointUnlocking: filterExercises("déverrouillage articulaire"),
warmup: filterExercises("échauffement"),
musculation: filterExercises("musculation"),
sheathing: filterExercises("gainage"),
cardio: filterExercises("cardio"),
competitionPreparation: filterExercises("préparation au concours"),
abs: filterExercises("abdominaux"),
cardioWarmUp: filterExercises("cardio échauffement"),
}
}
Unit tests
function logCategoryLength(userExercises: WKGExercises): void {
Object.values(userExercises).forEach((ex, index) => {
console.log(Object.keys(userExercises)[index], ex.length)
})
}
const allSimpleUserCases = generateSimpleAllUserCase()
describe("getUserExercises", () => {
test("BEGINNER INDOOR User get exercises in each categories", () => {
allSimpleUserCases.forEach((userArgs) => {
const userExercises = getUserExercises(userArgs)
logCategoryLength(userExercises)
const isAllCategoryHaveLength = Object.values(userExercises).every(
(exercises) => exercises.length > 0
)
expect(isAllCategoryHaveLength).toBeTruthy()
})
})
})
CodePudding user response:
There's a lot of debate on this issue, and it's one of those "everyone's kind of right" kind of things. It's very much like tabs vs spaces where the only real solution is whiskey.
In short, many people argue that you should only test your public API - that the private implementation should be able to change without breaking tests. The argument is that the internal implementation shouldn't matter, only the consumable "public" portion really matters.
Other people want to write good solid methods which are not part of the public API but should be hardened and tested to provide a reliable internal API which developers can rely on.
I fall into "both" camps, and my solution is to pull the "private" methods into a separate "utils" file. This kind of makes the methods "public" (eg. anybody can import them), but I have found that people tend to not import methods they don't need. You can then write tests against the utils file separately. I have even found that some of these methods become useful in other places over time and you don't need to do any refactoring for others to be able to use them.
I highly advise against using things like rewire
which can allow you to access private/non-exported methods. It just makes your tests more confusing and more difficult to maintain.