Oto trywialny przykład, który ilustruje sedno mojego problemu:
var innerLib = require('./path/to/innerLib');
function underTest() {
return innerLib.doComplexStuff();
}
module.exports = underTest;
Próbuję napisać test jednostkowy dla tego kodu. Jak mogę wyszydzić wymaganie dla innerLib
bez require
całkowitego wyszydzania funkcji?
Więc to ja próbuję wyszydzić globalny require
i odkryć, że nawet to nie zadziała:
var path = require('path'),
vm = require('vm'),
fs = require('fs'),
indexPath = path.join(__dirname, './underTest');
var globalRequire = require;
require = function(name) {
console.log('require: ' + name);
switch(name) {
case 'connect':
case indexPath:
return globalRequire(name);
break;
}
};
Problem polega na tym, że require
funkcja wewnątrz underTest.js
pliku nie została w rzeczywistości wyszydzona. Nadal wskazuje na funkcję globalną require
. Wygląda więc na to, że mogę wyszydzać require
funkcję tylko w tym samym pliku, w którym robię mockowanie. Jeśli użyję globalnego require
do uwzględnienia czegokolwiek, nawet po nadpisaniu lokalnej kopii, wymagane pliki nadal będą miały globalne require
odniesienie.
javascript
node.js
mocking
Matthew Taylor
źródło
źródło
global.require
. Zmienne zapisywanemodule
domyślnie, ponieważ moduły mają zasięg modułu.Odpowiedzi:
Możesz teraz!
Opublikowałem proxyquire, który zajmie się nadpisaniem globalnego wymagania wewnątrz twojego modułu podczas testowania go.
Oznacza to, że nie potrzebujesz żadnych zmian w kodzie , aby wstrzyknąć makiety dla wymaganych modułów.
Proxyquire ma bardzo prosty interfejs API, który umożliwia rozwiązanie modułu, który próbujesz przetestować, i przekazanie makiet / kodów pośredniczących do wymaganych modułów w jednym prostym kroku.
@Raynos ma rację, że tradycyjnie musiałeś uciekać się do niezbyt idealnych rozwiązań, aby to osiągnąć lub zamiast tego rozwijać oddolny
To jest główny powód, dla którego stworzyłem proxyquire - aby umożliwić odgórne tworzenie sterowane testami bez żadnych kłopotów.
Zapoznaj się z dokumentacją i przykładami, aby ocenić, czy będzie pasować do Twoich potrzeb.
źródło
proxyquire
!Lepszą opcją w tym przypadku jest mockowanie metod zwracanego modułu.
Co więcej, większość modułów node.js jest singletonami; dwa fragmenty kodu, które wymagają () tego samego modułu, otrzymują to samo odwołanie do tego modułu.
Możesz to wykorzystać i użyć czegoś takiego jak sinon, aby wyszydzić wymagane przedmioty. test mokki następujący:
Sinon ma dobrą integrację z chai do tworzenia twierdzeń, a ja napisałem moduł integrujący sinon z mokką, aby umożliwić łatwiejsze czyszczenie szpiegów / stubów (aby uniknąć zanieczyszczenia testowego).
Zauważ, że underTest nie może być mockowany w ten sam sposób, ponieważ underTest zwraca tylko funkcję.
Inną opcją jest użycie Jest mocks. Śledź na ich stronie
źródło
require('some_module')
, ponieważ cały twój kod ma ten sam katalog node_modules. Po drugie, artykuł łączy przestrzeń nazw z singletonami, co jest trochę ortogonalne. Po trzecie, ten artykuł jest cholernie stary (jeśli chodzi o node.js), więc to, co mogło obowiązywać w tamtych czasach, prawdopodobnie nie jest aktualne.innerLib.toCrazyCrap.restore()
i restub lub zadzwoń sinon poprzezsinon.stub(innerLib, 'toCrazyCrap')
który pozwala zmienić sposób zachowuje en:innerLib.toCrazyCrap.returns(false)
. Ponadto rewire wydaje się być bardzo podobny doproxyquire
powyższego rozszerzenia.Używam mock-require . Upewnij się, że zdefiniowałeś swoje makiety przed
require
testowaniem modułu.źródło
Mocking
require
to dla mnie nieprzyjemny hack. Osobiście starałbym się tego uniknąć i zmienić kod, aby był bardziej testowalny. Istnieją różne podejścia do obsługi zależności.1) przekazywać zależności jako argumenty
Dzięki temu kod będzie uniwersalnie testowalny. Wadą jest to, że musisz przekazywać zależności, co może sprawić, że kod będzie wyglądał na bardziej skomplikowany.
2) zaimplementuj moduł jako klasę, a następnie użyj metod / właściwości klasy, aby uzyskać zależności
(To jest wymyślony przykład, w którym użycie klas nie jest rozsądne, ale przekazuje ideę) (przykład z ES6)
Teraz możesz łatwo
getInnerLib
przetestować kod. Kod staje się bardziej szczegółowy, ale także łatwiejszy do przetestowania.źródło
colors
moduł, z którym się mieszaString.prototype
)Jeśli kiedykolwiek używałeś żartu, prawdopodobnie znasz funkcję kpiny.
Używając "jest.mock (...)" możesz po prostu określić ciąg znaków, który wystąpiłby w instrukcji wymagania w twoim kodzie, a gdy moduł jest wymagany przy użyciu tego ciągu, zamiast tego zostanie zwrócony obiekt pozorowany.
Na przykład
całkowicie zamieni wszystkie importy / wymagania "firebase-admin" na obiekt, który zwróciłeś z tej funkcji "fabryki".
Cóż, możesz to zrobić używając żart, ponieważ jest tworzy środowisko uruchomieniowe wokół każdego uruchamianego modułu i wstrzykuje "przechwyconą" wersję wymagania do modułu, ale nie byłbyś w stanie tego zrobić bez żartu.
Próbowałem to osiągnąć za pomocą pozornego wymagania, ale dla mnie nie działało to na zagnieżdżonych poziomach w moim źródle. Spójrz na następujący problem na githubie: mock-require nie zawsze wywoływany z Mocha .
Aby rozwiązać ten problem, stworzyłem dwa moduły npm, których możesz użyć do osiągnięcia tego, co chcesz.
Potrzebujesz jednej wtyczki babel i modułu mocker.
W swoim .babelrc użyj wtyczki babel-plugin-mock-require z następującymi opcjami:
aw pliku testowym użyj modułu jestlike-mock w następujący sposób:
jestlike-mock
Moduł jest nadal bardzo elementarny i nie ma dużo dokumentacji, ale nie ma zbyt wiele kodu albo. Doceniam wszelkie PR za pełniejszy zestaw funkcji. Celem byłoby odtworzenie całej funkcji „jest.mock”.Aby zobaczyć, jak jest to implementowane, można wyszukać kod w pakiecie "jest-runtime". Zobacz na przykład https://github.com/facebook/jest/blob/master/packages/jest-runtime/src/index.js#L734 , tutaj generują "automock" modułu.
Mam nadzieję, że to pomoże;)
źródło
Nie możesz. Musisz zbudować swój zestaw testów jednostkowych tak, aby najniższe moduły były testowane jako pierwsze, a moduły wyższego poziomu, które wymagają modułów, były testowane później.
Musisz także założyć, że każdy kod innej firmy i sam node.js są dobrze przetestowane.
Przypuszczam, że w najbliższej przyszłości pojawią się fałszywe frameworki, które nadpiszą
global.require
Jeśli naprawdę musisz wstrzyknąć makietę, możesz zmienić kod, aby ujawnić zakres modułowy.
Ostrzegamy, że ujawnia się to
.__module
w twoim API, a każdy kod może uzyskać dostęp do zakresu modułowego na własne niebezpieczeństwo.źródło
Możesz użyć fałszywej biblioteki:
źródło
Prosty kod do mockowania modułów dla ciekawskich
Zauważ części gdzie manipulować
require.cache
i uwagarequire.resolve
metodę, gdyż jest to tajny sos.Użyj jak :
ALE ... proxyquire jest całkiem niezłe i powinieneś tego używać. Utrzymuje twoje zastąpienia wymagań zlokalizowane tylko do testów i bardzo to polecam.
źródło