javascript - Cea mai bună metodă de a testa IIFE (Funcție Invocată Imediat)
Deci am o aplicație existentă care folosește extensiv IIFE-uri în browser. Încerc să introduc niște teste unitare în cod și să păstrez modelul de IIFE pentru actualizările noi ale bazei de cod. Cu toate acestea, am probleme chiar și în a scrie un test care să îmi ofere acces la cod. De exemplu, văd acest tip de logică peste tot în baza de cod:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
return router;
})(Router || {});
Apoi fișierul JS este inclus într-un tag script în marcaj:
<script src="js/RouteUser.js"></script>
și apelat astfel în codul de producție:
Router.routeUser(myUser)
Deci întrebarea mea este, cum scriu un test care să testeze metoda routeUser? Am încercat asta în testul meu Mocha:
var router = require('../../main/resources/public/js/RouteUser');
suite('Route User Tests', function () {
test('Route The User', function () {
if (!router)
throw new Error("failed!");
else{
router.routeUser("Me")
}
});
});
Dar primesc o excepție:
TypeError: router.routeUser is not a function
at Context.<anonymous> (src\test\js\RouteUser.test.js:8:20)
Apoi am încercat să returnez metoda, ceea ce dă aceeași eroare:
var Router = (function (router) {
return {
routeUser: function (user) {
console.log("I'm in! --> " + user)
}
}
}
)(Router || {});
Poate cineva să mă îndrume în direcția corectă aici?
Răspuns #1
Sună că...
- aveți o bază de cod de scripturi care sunt folosite doar în contextul browserului (utilizarea IIFE sugerează acest lucru)
- ai dori să introduci teste unitare fără browser (Jest, Mocha?) folosind node.js (idee bună!)
- dar probabil nu vrei să migrezi întregul cod sursă la un stil de codare diferit în acest moment (poate fi mult de lucru, în funcție de dimensiunea codului tău)
Având în vedere aceste presupuneri, problema este că vrei ca codul tău să...
- acționează ca un script atunci când este utilizat în producție (setează
window.Routerglobal etc) - acționează ca un modul atunci când este folosit în teste unitare astfel încât să îl poți
require()în testele unitare
UMD
UMD, sau definiția universală a modulului, obișnuia să fie o modalitate comună de a scrie cod astfel încât să funcționeze în mai multe medii. Abordare interesantă, dar foarte incomodă, și îmi place să cred că UMD este ceva din trecut în zilele noastre...
O voi lăsa aici pentru completitudine.
Doar ia ideea UMD
Dacă singurul lucru pe care vrei să-l faci pentru moment este să faci ca un anumit script să funcționeze și ca modul, astfel încât să poată fi importat în teste, ai putea face o mică ajustare:
var Router = (function (router) {
router.routeUser = function(user) {
console.log("I'm in! --> " + user)
};
if (typeof exports === "object") {
module.exports = router;
// now the Mocha tests can import it!
}
return router;
})(Router || {});
Soluție pe termen lung
Pe termen lung, poți obține multe beneficii rescriind tot codul tău pentru a folosi DOAR module și folosind un instrument precum webpack pentru a-l împacheta pentru tine. Ideea de mai sus este un pas mic în direcția ta care îți oferă un beneficiu specific (testabilitate). Dar nu este o soluție pe termen lung și vei întâmpina unele probleme în gestionarea dependențelor (ce se întâmplă dacă Router-ul tău se așteaptă ca unele elemente globale să fie disponibile?)
Răspuns #2
Dacă intenționați să rulați testele Mocha în browser, nu trebuie să modificați codul existent.
Să parcurgem modelul IIFE, pentru că pe baza codului tău, cred că s-ar putea să nu înțelegi cum funcționează. Forma de bază este aceasta:
var thing = (function() {
return 1;
})();
console.log(thing) // '1'
Este o declarație var care setează thing egal cu valoarea din partea dreaptă a semnului egal. Pe partea dreaptă, primul set de paranteze înfășoară o funcție. Apoi, un al doilea set de paranteze stă chiar lângă ea, la sfârșit. Al doilea set invocă expresia funcției conținută în primul set de paranteze. Asta înseamnă că valoarea return a funcției va fi valoarea din partea dreaptă a declarației var. Deci thing este egal cu 1.
În cazul tău, asta înseamnă că variabila exterioară Router este setată egal cu variabila router returnată de funcția ta. Asta înseamnă că o poți accesa ca Router în testele tale, după includerea scriptului în DOM:
suite('Route User Tests', function () {
test('Route The User', function () {
if (!Router) // <- Notice the capital 'R'
throw new Error("failed!");
else {
Router.routeUser("Me") // <- capital 'R'
}
});
});
Dacă intenționați să rulați testele cu node, vedeți răspunsul lui Kos.
