Node.js mit TSOA und Authentifizierung - (Teil 8) Der Unit-Test
Ich bereite die Express-Applikation für den ersten Unit-Test vor. Dank der bisherigen Vorbereitung kann das Versprechen vom ersten Artikel eingelöst werden: einzeilige Absicherung und einzeiliges Testen der Absicherung. So bauen wir künftig bessere Applikationen. Test frei!
In diesem Artikel schreiben wir den Unit-Test für den gesicherten Endpunkt. Der Vitest-Helfer ist vorbereitet. Somit bleiben der Test und das Setup für die Aufrufe. Bevor wir anfangen, empfiehlt es sich, die Vorgänger der Artikelserie zu lesen:
- Teil 1: Die Zielsetzung
- Teil 2: Das Basis-Setup
- Teil 3: Ein JWT erzeugen
- Teil 4: Header-Validierung in der AuthMiddleware
- Teil 5: Error-Middleware für standardkonforme Antworten
- Teil 6: Tokenvalidierung
- Teil 7: Die Unit-Test-Vorbereitung
Anpassung der Express-Applikation
Damit wir die Express-Applikation mithilfe von Supertest testen können, benötigen wir die Applikation als Export. Dafür erstellen wir eine Datei namens createApp.ts, deren Export unsere bisherige Express-Applikation liefert.
import express from 'express'
import { RegisterRoutes } from '@/routes'
import { DefaultMappingStrategy, MapperRegistry } from 'http-problem-details-mapper'
import { UnauthenticatedMapper } from '@/errors/UnauthenticatedError'
import { UnauthorizedMapper } from '@/errors/UnauthorizedError'
import { DefaultErrorMapper } from '@/errors/DefaultErrorMapper'
import HttpProblemResponse from '@/errors/HttpProblemResponse'
const createApp = (): express.Express => {
const mapperRegistry = new MapperRegistry()
.registerMapper(new UnauthenticatedMapper())
.registerMapper(new UnauthorizedMapper())
.registerMapper(new DefaultErrorMapper())
const ErrorMappingStrategy = new DefaultMappingStrategy(mapperRegistry)
const app = express()
RegisterRoutes(app)
// The RFC 7807 error middleware has to be the last app.use() to work as intended
// It will map the thrown errors to the RFC applying responses
app.use(HttpProblemResponse({strategy: ErrorMappingStrategy}))
return app
}
export default createApp
Das Ergebnis binden wir in index.ts ein:
import createApp from '@/createApp'
const PORT = process.env.PORT || 4000
const app = createApp()
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`)
})
Skript in der Package.json
Für die schnelle Ausführung der Tests aktualisieren wir die package.json um folgende zwei Zeilen:
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
},
Die erste führt die Tests einmalig aus, die zweite lässt sie im Watch-Mode laufen. Letzterer eignet sich für testgetriebene Entwicklung.
Der eigentliche Unit-Test
Wir haben soweit alles vorbereitet und schreiben jetzt den Unit-Test. Im Rahmen des Tests mocken wir die Authentication-Middleware, binden unsere Express-Applikation in Supertest ein und simulieren die Aufrufe. Der Mock stellt sicher, dass wir nicht die eigentliche Middleware prüfen, sondern nur den korrekten Aufruf. Die Middleware-Tests folgen später.
Hier ist der Unit-Test in seiner Gesamtheit mit allen Imports und dem Mock.
import supertest from 'supertest'
import createApp from '@/createApp'
import { expressAuthentication } from '@/services/AuthMiddleware'
import { MockedFunction } from 'vitest'
const agent = supertest.agent(createApp())
vi.mock('@/services/AuthMiddleware')
const mockExpressAuthentication = expressAuthentication as MockedFunction<typeof expressAuthentication>
describe('EntityController', () => {
it('should validate secure route is accessible by ADMIN', async () => {
// act
await agent.get('/entities')
// assert
expect(mockExpressAuthentication).toHaveBeenCalledWithRequiredScopes(['ADMIN'])
})
})
Fazit
Mit dem Aufruf der Tests können wir sicherstellen, dass die @Security-Deklaration den Erwartungen entspricht. Der Aufruf von npm run test liefert das passende Ergebnis:
❯ npm run test
> nodejs_tsoa_auth@1.0.0 test
> vitest run
RUN v4.0.16 /Users/markschmeiser/WebstormProjects/nodejs_tsoa_auth
✓ tests/entities/EntitiyController.spec.ts (1 test) 10ms
✓ EntityController (1)
✓ should validate secure route is accessible by ADMIN 10ms
Test Files 1 passed (1)
Tests 1 passed (1)
Start at 15:52:41
Duration 457ms (transform 65ms, setup 19ms, import 353ms, tests 10ms, environment 0ms)
In einem weiteren Artikel werde ich die Express-Middleware prüfen. Die korrekte Ausführung der Scope-Vergleiche ist entscheidend für die Sicherheit der Applikation.