Zastanawiam się nad podejściem do testowania prywatnych części kodu (przykłady w JS, ale dotyczy to każedgo języka obsługującego domknięcia).
Załóżmy, że mamy taki moduł (tak by wyglądał gdybym nie myślał o testach):
/**
* @module doSomething
*/
function doSomething(arg) {
// code...
const partialResult1 = createPartialResult1(someArg);
const partialResult2 = createPartialResult2(anotherArg);
// code ...
return result;
}
function createPartialResult1(arg) {
// code...
return part;
}
function createPartialResult2(arg) {
// code...
return part;
}
export default doSomething;
Mam upublicznioną tylko jedną funkcję - doSomething
, pozostałe funkcje są niewidoczne dla kodu spoza modułu - wg mnie tak właśnie powinno być, wystawiam minimalny potrzebny interfejs.
No tu pojawia się problem, niektóre funkcje prywatne są na tyle skomplikowane, że chciałbym je przetestować (realny przykład nad którym teraz pracuję to funkcja parsująca zapytanie JSON do AST, funkcje prywatne tworzą poszczególne segmenty AST + helpery). No i widzę kilka opcji:
-
Mimo wszystko nie testować prywatnych funkcji - dla mnie odpada, mimo że jest to niby szczegół implementacyjny to takie testy znacząco ułatwiają mi pisanie kodu.
-
Wyciągnąć prywatne funkcje do osobnego modułu i upublicznić je:
/**
* @module partsCreators
*/
function createPartialResult1(arg) {
// code...
return part;
}
function createPartialResult2(arg) {
// code...
return part;
}
export {
createPartialResult1,
createPartialResult2,
};
/**
* @module doSomething
*/
import {
createPartialResult1,
createPartialResult2,
} from 'partsCreators';
function doSomething(arg) {
// code...
const partialResult1 = createPartialResult1(someArg);
const partialResult2 = createPartialResult2(anotherArg);
// code ...
return result;
}
export default doSomething;
Mogę je teraz sobie łatwo przetestować, ale boli mnie trochę taka zmiana kodu pod testy, no ale aktualnie stosuję właśnie to podejście, ze względu na jego prostotę.
- Pozostawienie funkcji jako prywatnych + testy do nich. Jako że mamy domknięcie, to nie dobiorę się do funkcji prywatnych przez żadną refleksję itp, ale mogę warunkowo upublicznić elementy na cele testów, używając np plugina do Babela:
/**
* @module doSomething
*/
function doSomething(arg) {
// code...
const partialResult1 = createPartialResult1(someArg);
const partialResult2 = createPartialResult2(anotherArg);
// code ...
return result;
}
function createPartialResult1(arg) {
// code...
return part;
}
function createPartialResult2(arg) {
// code...
return part;
}
// It will be stripped by Babel if not running by testing tool
export const __test__ = {
createPartialResult1,
createPartialResult2,
};
export default doSomething;
Co powinno (wg mnie) być prywatne, pozostaje prywatne, ale dochodzi dodatkowy etap transpilacji kodu (lub w ogóle dochodzi transpilacja, jeśli wcześniej nie była potrzebna).
Którą metodę byście zastosowali? Może macie jeszcze jakieś inne pomysły?