h3 icon indicating copy to clipboard operation
h3 copied to clipboard

question: how to declaratively create `H3Event`'s to test event handlers

Open juliusmarminge opened this issue 1 year ago • 3 comments

Describe the change

How should one create H3Event's in order to test their event handlers? Assume I just want to run this event handler as a function, without having to create a server and listening to it? I came up with a workaround to use the web-converter and passing it a Request instead, but that feels a bit "hacky" I think

CleanShot 2024-02-25 at 23 58 07@2x

I'd just want to be able to do something like

eventHandler(new H3Event({ /** some options */}))

but the constructor args are very confusing for this use-case

URLs

No response

Additional information

  • [ ] Would you be willing to help?

juliusmarminge avatar Feb 25 '24 23:02 juliusmarminge

Was just looking for this myself =)

PrimeTimeTran avatar May 12 '24 19:05 PrimeTimeTran

It's very tricky to test h3 without listening to a server.

I did have success with the following:

import { createEvent } from 'h3'

const req = <IncomingMessage>{
  method: 'POST',
  path: '...',
  headers: new Headers(...),
  body: {
    ...
  }
}

const res = <ServerResponse>{}

const event = createEvent(req, res)

// Some internal stuff that h3 does, sometimes needed, sometimes not
event._method = req.method
event._path = req.path
event._headers = req.headers
if (req.body) {
  event._requestBody = req.body
}

// Run the event logic
await app(...).use(...)(event)

// However now you have to deal with handling the raw response object, which is extortionary annoying
expect(res.status).toBe(200) // will most likely fail

Working with https://github.com/ladjs/supertest make things easier and way more reliable.

An example with Vite:

// some.test.ts
import type { App } from 'h3'
import { createApp, eventHandler, toNodeListener } from 'h3'

import type { SuperTest, Test } from 'supertest'
import supertest from 'supertest'

describe('', () => {
  let app: App
  let request: SuperTest<Test>

  beforeEach(() => {
    app = createApp({ debug: false })
    request = supertest(toNodeListener(app))
  })

  it('', async () => {
    app.use('/api/test', eventHandler(() => {
      return 'hello world'
    }))

    const result = await request.get('/api/test').send()

    expect(result.text).toBe('hello world')
  })
})

There are plenty more examples you can look at: https://github.com/unjs/h3/tree/main/test

lucacicada avatar May 17 '24 07:05 lucacicada

so that's basically what I had just with the node listener instead of the web handlers

juliusmarminge avatar May 17 '24 07:05 juliusmarminge

Hi dear @juliusmarminge. With upcoming h3 v2, mockEvent can be used to easily create a mocked (web API backed) event for testing. we use it internally too.

pi0 avatar Jul 18 '24 20:07 pi0