A menudo tengo funciones que necesitan datos de un archivo de configuración o una base de datos. Preferiría funciones puras tanto como sea posible y algo que pueda probarse por unidad.
No tengo idea de cómo hacer esto correctamente, ya que no tengo un título en informática. Las opciones que puedo ver son:
Ejemplo 1:
function myFunc(p1, p2, db, configFile) { ... } pero eso significa que todas las demás funciones que llaman a myFunc() también deben tener db, configFile como argumentos.
Ejemplo 2:
function myFunc(p1, p2) { const config = readConfig(); dbConn = openDb(); ... }¿Supongo que esto hace que las pruebas unitarias sean imposibles?
Ejemplo 3:
const struct = { config: readConfig(), dbConn: openDb(), ... }; function myFunc(p1, p2, struct) { ... } Esto tiene la desventaja de que cuando los datos de otras funciones también se almacenan en esta struct global, cada función tiene acceso a más de lo que necesita.
Pregunta
¿Hay otras posibilidades? ¿Cuál es el enfoque recomendado?
Defina myFunc() así para mantenerlo puro:
function myFunc(p1, p2, readConfig, openDb) { ... }y llamarlo en un ámbito cerrado
// other code here { const config = readConfig(); const dbConn = openDb(); myFunc(p1, p2, readConfig, openDb); } // other code here // config and dbConn isn't available hereUna prueba de concepto:
function func(a, b) { console.log(a); console.log(b); } const a = undefined; const b = undefined; { const a = 'private1'; const b = 'private2'; func(a, b); } console.log(a); console.log(b);Creo que puedes usar Inyección de Dependencia aquí. Algo como esto:
function readConfig() { // returns real config } function openDb() { // return real db connection } function readConfigMock() { // returns mock config } function openDbMock() { // return mock db connection } function myFuncBuilder(readConfigFn, openDbFn) { return function(p1, p2) { // can use readConfigFn everywhere because of closure const config = readConfigFn(); dbConn = openDbFn(); ... } } const myFunc = myFuncBuilder(readConfig, openDb); // no need to know about openDb and readConfig function topFunc() { myFunc(p1, p2); } // code const myFunc = myFuncBuilder(readConfig, openDb); // test const myFunc = myFuncBuilder(readConfigMock, openDbMock); El truco está en pasar una función como argumento. Además, existe una función de orden superior (función que devuelve una función) llamada myFuncBuilder . Esta función crea myFunc en función de 2 argumentos: función para la conexión db y función para la configuración.
Por lo tanto, puede crear fácilmente simulacros de readConfig y openDb para realizar pruebas.
PS Además, el paradigma de la Programación Funcional nos enseña a usar mónadas IO en tales casos. Pero usar mónadas en JS/TS tiene una curva de aprendizaje muy pronunciada.