Running vitest fails after creating a Hono Cloudflare project
What version of Hono are you using?
4.2.4
What runtime/platform is your app running on?
Cloudflare Workers
What steps can reproduce the bug?
Repo for reproduction: https://github.com/callmetwan/hono-cloudflare-pages-vitest
npm create hono@latest hono-cloudflare-pages-vitest
Need to install the following packages:
[email protected]
Ok to proceed? (y) y
create-hono version 0.6.3
✔ Using target directory … hono-cloudflare-pages-vitest
? Which template do you want to use? cloudflare-pages
✔ Cloning the template
? Do you want to install project dependencies? yes
? Which package manager do you want to use? yarn
✔ Installing project dependencies
🎉 Copied project files
Get started with: cd hono-cloudflare-pages-vitest
Install vitest
yarn add -D vitest
Add to package.json
"scripts: {
. . .
"test": "vitest"
},
Create a test
// indext.test.tsx
import {test, expect} from "vitest";
test("should pass", () => {
expect(1 + 1).toBe(2);
})
Run tests
yarn test
Failure:
FAIL src/index.test.tsx [ src/index.test.tsx ]
TypeError: Cannot read properties of undefined (reading 'test')
❯ src/index.test.tsx:3:1
1| import {test, expect} from "vitest";
2|
3| test("should pass", () => {
| ^
4| expect(1 + 1).toBe(2);
5| })
Commenting out the build() plugin in vite.config.ts results in tests passing. Something in the plugin is causing an issue.
What is the expected behavior?
Tests should pass, the test method should be defined
What do you see instead?
FAIL src/index.test.tsx [ src/index.test.tsx ]
TypeError: Cannot read properties of undefined (reading 'test')
❯ src/index.test.tsx:3:1
1| import {test, expect} from "vitest";
2|
3| test("should pass", () => {
| ^
4| expect(1 + 1).toBe(2);
5| })
Additional information
Logging the test method in the test file does actually show the the test method is defined at some point:
stdout | src/index.test.tsx:2:9
{
test: [Function: chain2] {
each: [Function (anonymous)],
skipIf: [Function (anonymous)],
runIf: [Function (anonymous)],
extend: [Function (anonymous)],
withContext: [Function (anonymous)],
setContext: [Function (anonymous)],
mergeContext: [Function (anonymous)],
fn: [Function (anonymous)] {
each: [Function (anonymous)],
skipIf: [Function (anonymous)],
runIf: [Function (anonymous)],
extend: [Function (anonymous)]
}
}
}
I'm happy to try and help debug this further, but I am very unfamiliar with the internals of both hono and vitest and would need some guidance on where to start
Hi @callmetwan
How about using this vitest.config.ts?
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
globals: true
}
})
Well, that solves it by side stepping vite.config.ts which means the Cloudflare adapter is no longer being loaded. This also works:
// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({});
Something about the cloudflare adapter breaks Vite's ability to recognize it's own testing module.
I view this as being important because it means bindings no longer work properly. You could make the argument that you could mock them, but having bindings that provide defaults is something many rely upon and not having access in a unit testing environment is limiting.
@callmetwan
You can use @cloudflare/vitest-pool-workers: https://developers.cloudflare.com/workers/testing/vitest-integration/
Oh wow, I had no idea that existed! Thank you for sharing.
This is definitely the answer, but now I am getting an error that I cannot tell if it is related to Vitest + Cloudflare + Hono or if this is something I have done incorrectly.
I am seeing that context.env is completely undefined. No bindings at all are loaded. I know vitest is loading my wrangler.toml file because @cloudflare/vitest-pool-workers threw an error indicating nodejs_compat needed to be enabled. After enabling the tests ran but context.env is undefined.
TypeError: Cannot read properties of undefined (reading 'd1primary')
I apologize if this has moved more into Cloudflare support; I can't tell if this is a Hono issue or something else. Here is my wrangler file:
wrangler.toml
compatibility_date = "2024-04-03"
compatibility_flags = [ "nodejs_compat" ]
[[d1_databases]]
binding = "d1primary"
database_name = "database" #real value redacted
database_id = "00000000-0000-0000-0000-000000000000" #real value redacted
migrations_dir = "./src/server/migrations"
vitest.config.ts
//vitest.config.ts
import { defineWorkersProject } from "@cloudflare/vitest-pool-workers/config";
export default defineWorkersProject({
test: {
poolOptions: {
workers: {
wrangler: {
configPath: "./wrangler.toml",
},
},
},
},
});
Is there anything obvious you see missing?
@callmetwan
How about using this config?
export default defineWorkersProject(async () => {
return {
test: {
poolOptions: {
workers: {
miniflare: {
compatibilityFlags: ['nodejs_compat'],
compatibilityDate: '2024-04-01',
d1Databases: ['DB'],
}
}
}
}
}
})
@yusukebe Unfortunately context.env is still undefined using that config :/
@callmetwan
Please refer to this project: https://github.com/yusukebe/testing-d1-app-with-types
@yusukebe Thank you for sharing that. I see that repo depicts client testing, it seems to work by hydrating a test client with the env from the cloudflare testing package.
Is there no way to do "backend' testing in the way the Hono docs show?
// index.ts
app.get('/posts', (c) => {
return c.text('Many posts')
})
app.post('/posts', (c) => {
return c.json(
{
message: 'Created',
},
201,
{
'X-Custom': 'Thank you',
}
)
})
// index.test.ts
describe('Example', () => {
test('GET /posts', async () => {
const res = await app.request('/posts')
expect(res.status).toBe(200)
expect(await res.text()).toBe('Many posts')
})
})
That is a test for a backend.
const client = testClient(api, env)
Is this not a test version of hc from hono/client?
Regardless, when I directly build a test against api in the test repo you created, it fails:
// api.test.ts
import api from '../src'
it("this tests with api directly", async () => {
const response = await api.request("/api/");
expect(response.status).toBe(200);
})
The output
stderr | test/api.test.ts > this tests with api directly
TypeError: Cannot read properties of undefined (reading 'DB')
Is this expected?
I think I found a solution for what I'm looking for:
import { describe, it } from "vitest";
import { env } from "cloudflare:test";
import app from "./index";
describe("Routing rules", () => {
it("should have a route for the home page", async ({ expect }) => {
const req = new Request("/", { method: "GET" });
const response = await app.fetch(req, env);
expect(response.status).toBe(200);
expect(response.body).toContain(`<div id="root">`);
});
});
This seems to work. For anyone else finding this, you'll need to install @cloudflare/vitest-pool-workers as a dev dependency. env is imported from a module that the vitest-pool-workers package makes available at runtime. See https://developers.cloudflare.com/workers/testing/vitest-integration/test-apis/#cloudflaretest-module-definition for more information