Używam Mocha do testowania jednostkowego aplikacji napisanej dla Node.js.
Zastanawiam się, czy możliwe jest testowanie jednostkowe funkcji, które nie zostały wyeksportowane w module.
Przykład:
Mam wiele funkcji zdefiniowanych w ten sposób w foobar.js
:
function private_foobar1(){
...
}
function private_foobar2(){
...
}
I kilka funkcji wyeksportowanych jako publiczne:
exports.public_foobar3 = function(){
...
}
Przypadek testowy ma następującą strukturę:
describe("private_foobar1", function() {
it("should do stuff", function(done) {
var stuff = foobar.private_foobar1(filter);
should(stuff).be.ok;
should(stuff).....
Oczywiście to nie działa, ponieważ private_foobar1
nie jest eksportowane.
Jaki jest prawidłowy sposób testowania prywatnych metod jednostkowych? Czy Mocha ma na to wbudowane metody?
Odpowiedzi:
Jeśli funkcja nie jest eksportowana przez moduł, nie można jej wywołać kodem testowym poza modułem. Wynika to z działania JavaScript, a Mocha sama nie może tego obejść.
W kilku przypadkach, w których stwierdziłem, że testowanie funkcji prywatnej jest właściwą rzeczą do zrobienia, ustawiłem pewną zmienną środowiskową, którą mój moduł sprawdza, aby określić, czy działa w konfiguracji testowej, czy nie. Jeśli działa w konfiguracji testowej, eksportuje dodatkowe funkcje, które mogę następnie wywołać podczas testowania.
Słowo „środowisko” jest tutaj używane luźno. Może to oznaczać sprawdzenie
process.env
lub coś innego, co może komunikować się z modułem „jesteś teraz testowany”. Przypadki , w których musiałem to zrobić, znajdowały się w środowisku RequireJS i użyłemmodule.config
w tym celu.źródło
SyntaxError: 'import' and 'export' may only appear at the top level
import
,export
wewnątrz bloku. W końcu będziesz w stanie osiągnąć tego rodzaju rzeczy w ES6 za pomocą programu ładującego system. Jednym ze sposobów obejścia tego problemu jest teraz użyciemodule.exports = process.env.NODE_ENV === 'production' ? require('prod.js') : require('dev.js')
i przechowywanie różnic w kodzie es6 w odpowiednich plikach.Sprawdź moduł rewire . Umożliwia pobieranie (i manipulowanie) zmiennymi prywatnymi i funkcjami w module.
Więc w twoim przypadku użycie byłoby takie jak:
var rewire = require('rewire'), foobar = rewire('./foobar'); // Bring your module in with rewire describe("private_foobar1", function() { // Use the special '__get__' accessor to get your private function. var private_foobar1 = foobar.__get__('private_foobar1'); it("should do stuff", function(done) { var stuff = private_foobar1(filter); should(stuff).be.ok; should(stuff).....
źródło
Cannot find module '../../package' from 'node.js'
. Czy ktoś to zna?.ts
,typescript
biegnę za pomocąts-node
@cluOto naprawdę dobry przepływ pracy do przetestowania prywatnych metod, wyjaśniony przez Philipa Waltona, inżyniera Google na swoim blogu.
Zasada
_
(na przykład)Następnie użyj zadania kompilacji lub własnego systemu kompilacji (na przykład kodu grunt-strip ), aby rozebrać ten blok do kompilacji produkcyjnych.
Twoje kompilacje testowe mają dostęp do prywatnego interfejsu API, a wersje produkcyjne nie.
Skrawek
Wpisz swój kod w ten sposób:
var myModule = (function() { function foo() { // Private function `foo` inside closure return "foo" } var api = { bar: function() { // Public function `bar` returned from closure return "bar" } } /* test-code */ api._foo = foo /* end-test-code */ return api }())
I twoje zadania Grunt, takie jak to:
grunt.registerTask("test", [ "concat", "jshint", "jasmine" ]) grunt.registerTask("deploy", [ "concat", "strip-code", "jshint", "uglify" ])
Głębiej
W późniejszym artykule wyjaśnia „dlaczego” „testowanie metod prywatnych”
źródło
Jeśli wolisz, aby było to proste, po prostu wyeksportuj również prywatne elementy członkowskie, ale wyraźnie oddziel je od publicznego API pewną konwencją, np. Dodaj do nich prefiks
_
lub zagnieżdżaj je pod pojedynczym obiektem prywatnym .var privateWorker = function() { return 1 } var doSomething = function() { return privateWorker() } module.exports = { doSomething: doSomething, _privateWorker: privateWorker }
źródło
Dodałem dodatkową funkcję, którą nazywam Internal () i stamtąd zwracam wszystkie funkcje prywatne. Ta funkcja Internal () jest następnie eksportowana. Przykład:
function Internal () { return { Private_Function1, Private_Function2, Private_Function2} } // Exports -------------------------- module.exports = { PublicFunction1, PublicFunction2, Internal }
Możesz wywołać funkcje wewnętrzne w następujący sposób:
let test = require('.....') test.Internal().Private_Function1()
Najbardziej podoba mi się to rozwiązanie, ponieważ:
źródło
W tym celu stworzyłem pakiet npm, który może ci się przydać: require-from
Zasadniczo ujawniasz metody niepubliczne poprzez:
module.testExports = { private_foobar1: private_foobar1, private_foobar2: private_foobar2, ... }
Uwaga:
testExports
może to być dowolna prawidłowa nazwa, z wyjątkiemexports
oczywiście.I z innego modułu:
var requireFrom = require('require-from'); var private_foobar1 = requireFrom('testExports', './path-to-module').private_foobar1;
źródło
requireFrom
z odpowiednimi parametrami.) Ponadto, jeśli moduł withtextExports
zostanie załadowany przezrequire
wywołanie przed jegorequireFrom
załadowaniem,requireFrom
zwróciundefined
. (Właśnie to przetestowałem.) Chociaż często można kontrolować kolejność ładowania modułów, nie zawsze jest to praktyczne. (O czym świadczą niektóre pytania Mocha na SO.) To rozwiązanie również nie będzie działać z modułami typu AMD. (Codziennie ładuję moduły AMD w Node do testów.)Wiem, że niekoniecznie jest to odpowiedź, której szukasz, ale stwierdziłem, że w większości przypadków, jeśli funkcja prywatna jest warta przetestowania, warto być w jej własnym pliku.
Np. Zamiast mieć prywatne metody w tym samym pliku co publiczne, na przykład ...
src / thing / PublicInterface.js
function helper1 (x) { return 2 * x; } function helper2 (x) { return 3 * x; } export function publicMethod1(x) { return helper1(x); } export function publicMethod2(x) { return helper1(x) + helper2(x); }
... podzielisz to w ten sposób:
src / thing / PublicInterface.js
import {helper1} from './internal/helper1.js'; import {helper2} from './internal/helper2.js'; export function publicMethod1(x) { return helper1(x); } export function publicMethod2(x) { return helper1(x) + helper2(x); }
src / thing / internal / helper1.js
export function helper1 (x) { return 2 * x; }
src / thing / internal / helper2.js
export function helper2 (x) { return 3 * x; }
W ten sposób możesz łatwo testować
helper1
ihelper2
tak jak jest, bez używania Rewire i innych "magicznych" (które, jak odkryłem, mają swoje własne problemy podczas debugowania lub gdy próbujesz przejść w kierunku TypeScript, nie wspominając o gorszej zrozumiałość dla nowych kolegów). A umieszczenie ich w podfolderze o nazwieinternal
lub czymś podobnym pomoże uniknąć przypadkowego użycia ich w niezamierzonych miejscach.PS: Innym częstym problemem związanym z metodami „prywatnymi” jest to, że jeśli chcesz przetestować
publicMethod1
ipublicMethod2
wyśmiać pomocników, zwykle potrzebujesz do tego czegoś takiego jak Rewire. Jeśli jednak znajdują się w osobnych plikach, możesz to zrobić za pomocą Proxyquire , który w przeciwieństwie do Rewire nie wymaga żadnych zmian w procesie kompilacji, jest łatwy do odczytania i debugowania oraz działa dobrze nawet z TypeScript.źródło
Poszedłem za odpowiedzią Barwina i sprawdziłem, jak można wykonać testy jednostkowe za pomocą modułu rewire . Mogę potwierdzić, że to rozwiązanie po prostu działa.
Moduł powinien składać się z dwóch części - publicznej i prywatnej. W przypadku funkcji publicznych możesz to zrobić w standardowy sposób:
const { public_foobar3 } = require('./foobar');
W zakresie prywatnym:
const privateFoobar = require('rewire')('./foobar'); const private_foobar1 = privateFoobar .__get__('private_foobar1'); const private_foobar2 = privateFoobar .__get__('private_foobar2');
Aby dowiedzieć się więcej na ten temat, stworzyłem działający przykład z pełnym testowaniem modułów, testowanie obejmuje zakres prywatny i publiczny.
W celu uzyskania dalszych informacji zachęcam do zapoznania się z artykułem ( Jak przetestować prywatne funkcje modułu CommonJS ) w pełni opisującym temat. Zawiera przykłady kodu.
źródło
Aby udostępnić prywatne metody do testowania, robię to:
const _myPrivateMethod: () => {}; const methods = { myPublicMethod1: () => {}, myPublicMethod2: () => {}, } if (process.env.NODE_ENV === 'test') { methods._myPrivateMethod = _myPrivateMethod; } module.exports = methods;
źródło
test
ustawiana jest zmienna środowiskowa ?