CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
Testing.md482 linesDownload Raw Back to Guides
1<h1 style="text-align: center;">Fastify</h1>2 3# Testing4<a id="testing"></a>5 6Testing is one of the most important parts of developing an application. Fastify7is very flexible when it comes to testing and is compatible with most testing8frameworks (such as [Node Test Runner](https://nodejs.org/api/test.html),9which is used in the examples below).10 11## Application12 13Let's `cd` into a fresh directory called 'testing-example' and type `npm init14-y` in our terminal.15 16Run `npm i fastify && npm i pino-pretty -D`17 18### Separating concerns makes testing easy19 20First, we are going to separate our application code from our server code:21 22**app.js**:23 24```js25'use strict'26 27const fastify = require('fastify')28 29function build(opts={}) {30  const app = fastify(opts)31  app.get('/', async function (request, reply) {32    return { hello: 'world' }33  })34 35  return app36}37 38module.exports = build39```40 41**server.js**:42 43```js44'use strict'45 46const server = require('./app')({47  logger: {48    level: 'info',49    transport: {50      target: 'pino-pretty'51    }52  }53})54 55server.listen({ port: 3000 }, (err, address) => {56  if (err) {57    server.log.error(err)58    process.exit(1)59  }60})61```62 63### Benefits of using fastify.inject()64 65Fastify comes with built-in support for fake HTTP injection thanks to66[`light-my-request`](https://github.com/fastify/light-my-request).67 68Before introducing any tests, we will use the `.inject` method to make a fake69request to our route:70 71**app.test.js**:72 73```js74'use strict'75 76const build = require('./app')77 78const test = async () => {79  const app = build()80 81  const response = await app.inject({82    method: 'GET',83    url: '/'84  })85 86  console.log('status code: ', response.statusCode)87  console.log('body: ', response.body)88}89test()90```91 92First, our code will run inside an asynchronous function, giving us access to93async/await.94 95`.inject` ensures all registered plugins have booted up and our application is96ready to test. Finally, we pass the request method we want to use and a route.97Using await we can store the response without a callback.98 99 100 101Run the test file in your terminal `node app.test.js`102 103```sh104status code:  200105body:  {"hello":"world"}106```107 108 109 110### Testing with HTTP injection111 112Now we can replace our `console.log` calls with actual tests!113 114In your `package.json` change the "test" script to:115 116`"test": "node --test --watch"`117 118**app.test.js**:119 120```js121'use strict'122 123const { test } = require('node:test')124const build = require('./app')125 126test('requests the "/" route', async t => {127  t.plan(1)128  const app = build()129 130  const response = await app.inject({131    method: 'GET',132    url: '/'133  })134  t.assert.strictEqual(response.statusCode, 200, 'returns a status code of 200')135})136```137 138Finally, run `npm test` in the terminal and see your test results!139 140The `inject` method can do much more than a simple GET request to a URL:141```js142fastify.inject({143  method: String,144  url: String,145  query: Object,146  payload: Object,147  headers: Object,148  cookies: Object149}, (error, response) => {150  // your tests151})152```153 154`.inject` methods can also be chained by omitting the callback function:155 156```js157fastify158  .inject()159  .get('/')160  .headers({ foo: 'bar' })161  .query({ foo: 'bar' })162  .end((err, res) => { // the .end call will trigger the request163    console.log(res.payload)164  })165```166 167or in the promisified version168 169```js170fastify171  .inject({172    method: String,173    url: String,174    query: Object,175    payload: Object,176    headers: Object,177    cookies: Object178  })179  .then(response => {180    // your tests181  })182  .catch(err => {183    // handle error184  })185```186 187Async await is supported as well!188```js189try {190  const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object })191  // your tests192} catch (err) {193  // handle error194}195```196 197#### Another Example:198 199**app.js**200```js201const Fastify = require('fastify')202 203function buildFastify () {204  const fastify = Fastify()205 206  fastify.get('/', function (request, reply) {207    reply.send({ hello: 'world' })208  })209 210  return fastify211}212 213module.exports = buildFastify214```215 216**test.js**217```js218const { test } = require('node:test')219const buildFastify = require('./app')220 221test('GET `/` route', t => {222  t.plan(4)223 224  const fastify = buildFastify()225 226  // At the end of your tests it is highly recommended to call `.close()`227  // to ensure that all connections to external services get closed.228  t.after(() => fastify.close())229 230  fastify.inject({231    method: 'GET',232    url: '/'233  }, (err, response) => {234    t.assert.ifError(err)235    t.assert.strictEqual(response.statusCode, 200)236    t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8')237    t.assert.deepStrictEqual(response.json(), { hello: 'world' })238  })239})240```241 242### Testing with a running server243Fastify can also be tested after starting the server with `fastify.listen()` or244after initializing routes and plugins with `fastify.ready()`.245 246#### Example:247 248Uses **app.js** from the previous example.249 250**test-listen.js** (testing with [`undici`](https://www.npmjs.com/package/undici))251```js252const { test } = require('node:test')253const { Client } = require('undici')254const buildFastify = require('./app')255 256test('should work with undici', async t => {257  t.plan(2)258 259  const fastify = buildFastify()260 261  await fastify.listen()262 263   const client = new Client(264    'http://localhost:' + fastify.server.address().port, {265      keepAliveTimeout: 10,266      keepAliveMaxTimeout: 10267    }268  )269 270  t.after(() => {271    fastify.close()272    client.close()273  })274 275  const response = await client.request({ method: 'GET', path: '/' })276 277  t.assert.strictEqual(await response.body.text(), '{"hello":"world"}')278  t.assert.strictEqual(response.statusCode, 200)279})280```281 282Alternatively, starting with Node.js 18,283[`fetch`](https://nodejs.org/docs/latest-v18.x/api/globals.html#fetch)284may be used without requiring any extra dependencies:285 286**test-listen.js**287```js288const { test } = require('node:test')289const buildFastify = require('./app')290 291test('should work with fetch', async t => {292  t.plan(3)293 294  const fastify = buildFastify()295 296  t.after(() => fastify.close())297 298  await fastify.listen()299 300  const response = await fetch(301    'http://localhost:' + fastify.server.address().port302  )303 304  t.assert.strictEqual(response.status, 200)305  t.assert.strictEqual(306    response.headers.get('content-type'),307    'application/json; charset=utf-8'308  )309  const jsonResult = await response.json()310  t.assert.strictEqual(jsonResult.hello, 'world')311})312```313 314**test-ready.js** (testing with315[`SuperTest`](https://www.npmjs.com/package/supertest))316```js317const { test } = require('node:test')318const supertest = require('supertest')319const buildFastify = require('./app')320 321test('GET `/` route', async (t) => {322  const fastify = buildFastify()323 324  t.after(() => fastify.close())325 326  await fastify.ready()327 328  const response = await supertest(fastify.server)329    .get('/')330    .expect(200)331    .expect('Content-Type', 'application/json; charset=utf-8')332  t.assert.deepStrictEqual(response.body, { hello: 'world' })333})334```335 336### How to inspect node tests3371. Isolate your test by passing the `{only: true}` option338```javascript339test('should ...', {only: true}, t => ...)340```3412. Run `node --test`342```bash343> node --test --test-only --node-arg=--inspect-brk test/<test-file.test.js>344```345- `--test-only` specifies to run tests with the `only` option enabled346- `--node-arg=--inspect-brk` will launch the node debugger3473. In VS Code, create and launch a `Node.js: Attach` debug configuration. No348   modification should be necessary.349 350Now you should be able to step through your test file (and the rest of351`Fastify`) in your code editor.352 353 354 355## Plugins356Let's `cd` into a fresh directory called 'testing-plugin-example' and type `npm init357-y` in our terminal.358 359Run `npm i fastify fastify-plugin`360 361**plugin/myFirstPlugin.js**:362 363```js364const fP = require("fastify-plugin")365 366async function myPlugin(fastify, options) {367    fastify.decorateRequest("helloRequest", "Hello World")368    fastify.decorate("helloInstance", "Hello Fastify Instance")369}370 371module.exports = fP(myPlugin)372```373 374A basic example of a Plugin. See [Plugin Guide](./Plugins-Guide.md)375 376**test/myFirstPlugin.test.js**:377 378```js379const Fastify = require("fastify");380const { test } = require("node:test");381const myPlugin = require("../plugin/myFirstPlugin");382 383test("Test the Plugin Route", async t => {384    // Create a mock fastify application to test the plugin385    const fastify = Fastify()386 387    fastify.register(myPlugin)388 389    // Add an endpoint of your choice390    fastify.get("/", async (request, reply) => {391        return ({ message: request.helloRequest })392    })393 394    // Use fastify.inject to fake a HTTP Request395    const fastifyResponse = await fastify.inject({396        method: "GET",397        url: "/"398    })399 400  console.log('status code: ', fastifyResponse.statusCode)401  console.log('body: ', fastifyResponse.body)402})403```404Learn more about [```fastify.inject()```](#benefits-of-using-fastifyinject).405Run the test file in your terminal `node test/myFirstPlugin.test.js`406 407```sh408status code:  200409body:  {"message":"Hello World"}410```411 412Now we can replace our `console.log` calls with actual tests!413 414In your `package.json` change the "test" script to:415 416`"test": "node --test --watch"`417 418Create the test for the endpoint.419 420**test/myFirstPlugin.test.js**:421 422```js423const Fastify = require("fastify");424const { test } = require("node:test");425const myPlugin = require("../plugin/myFirstPlugin");426 427test("Test the Plugin Route", async t => {428    // Specifies the number of test429    t.plan(2)430 431    const fastify = Fastify()432 433    fastify.register(myPlugin)434 435    fastify.get("/", async (request, reply) => {436        return ({ message: request.helloRequest })437    })438 439    const fastifyResponse = await fastify.inject({440        method: "GET",441        url: "/"442    })443 444    t.assert.strictEqual(fastifyResponse.statusCode, 200)445    t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })446})447```448 449Finally, run `npm test` in the terminal and see your test results!450 451Test the ```.decorate()``` and ```.decorateRequest()```.452 453**test/myFirstPlugin.test.js**:454 455```js456const Fastify = require("fastify");457const { test }= require("node:test");458const myPlugin = require("../plugin/myFirstPlugin");459 460test("Test the Plugin Route", async t => {461    t.plan(5)462    const fastify = Fastify()463 464    fastify.register(myPlugin)465 466    fastify.get("/", async (request, reply) => {467        // Testing the fastify decorators468        t.assert.ifError(request.helloRequest)469        t.assert.ok(request.helloRequest, "Hello World")470        t.assert.ok(fastify.helloInstance, "Hello Fastify Instance")471        return ({ message: request.helloRequest })472    })473 474    const fastifyResponse = await fastify.inject({475        method: "GET",476        url: "/"477    })478    t.assert.strictEqual(fastifyResponse.statusCode, 200)479    t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" })480})481```482