测试
对身份验证进行重复且一致的测试一直很棘手。特别是 OAuth 提供商,在自动化方式中很难测试,因为它们通常会引入额外的验证步骤,这些步骤会在您从新的地理位置、数据中心 IP 地址或新的用户代理等登录时触发。
为了克服这些限制,我们建议您使用以下策略之一来针对 Auth.js 应用程序运行成功的 E2E 测试。
- 使用 Keycloak 等软件运行您自己的 OAuth 提供商Keycloak
- 在开发模式下启用身份验证方法,如 Credentials 提供商
以下是每种策略的一个示例,利用 @playwright/test 用于自动化的 E2E 测试。
Keycloak
首先,设置一个 Keycloak 实例。然后您必须将 Keycloak 提供商 添加到您的 Auth.js 配置中。
此测试需要设置两个环境变量。这些凭据应该是用于可以针对您新创建的 Keycloak 实例进行身份验证的测试用户。
.env
TEST_KEYCLOAK_USERNAME=abc
TEST_KEYCLOAK_PASSWORD=123
然后我们可以使用 @playwright/test
来执行两个测试步骤。
- 这将访问登录 URL,输入身份验证凭据,然后单击“登录”按钮。它确保会话随后被正确设置。
- 这将单击“注销”按钮并确保会话随后为
null
。
tests/e2e/basic-auth.spec.ts
import { test, expect, type Page } from "@playwright/test"
test("Basic auth", async ({ page, browser }) => {
if (
!process.env.TEST_KEYCLOAK_USERNAME ||
!process.env.TEST_KEYCLOAK_PASSWORD
)
throw new TypeError("Incorrect TEST_KEYCLOAK_{USERNAME,PASSWORD}")
await test.step("should login", async () => {
await page.goto("https://127.0.0.1:3000/auth/signin")
await page.getByText("Keycloak").click()
await page.getByText("Username or email").waitFor()
await page
.getByLabel("Username or email")
.fill(process.env.TEST_KEYCLOAK_USERNAME)
await page.locator("#password").fill(process.env.TEST_KEYCLOAK_PASSWORD)
await page.getByRole("button", { name: "Sign In" }).click()
await page.waitForURL("https://127.0.0.1:3000")
const session = await page.locator("pre").textContent()
expect(JSON.parse(session ?? "{}")).toEqual({
user: {
email: "[email protected]",
name: "Bob Alice",
image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
},
expires: expect.any(String),
})
})
await test.step("should logout", async () => {
await page.getByText("Sign out").click()
await page
.locator("header")
.getByRole("button", { name: "Sign in", exact: true })
.waitFor()
await page.goto("https://127.0.0.1:3000/auth/session")
expect(await page.locator("html").textContent()).toBe("null")
})
})
Credentials
提供商在开发中
此方法需要较少的初始设置和维护,因为您不需要维护单独的 OAuth 提供商(如 Keycloak),但您也必须非常小心,不要在生产环境中使用不安全的身份验证方法。例如,在本例中,我们将添加一个 Credentials 提供商,它接受密码 password
。
auth.ts
import NextAuth from "next-auth"
import GitHub from "next-auth/providers/github"
import Credentials from "next-auth/providers/credentials"
const providers = [GitHub]
if (process.env.NODE_ENV === "development") {
providers.push(
Credentials({
id: "password",
name: "Password",
credentials: {
password: { label: "Password", type: "password" },
},
authorize: (credentials) => {
if (credentials.password === "password") {
return {
email: "[email protected]",
name: "Bob Alice",
image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
}
}
},
})
)
}
export const { handlers, auth } = NextAuth({
providers,
})
上面的配置示例将始终添加 GitHub 提供商,而 Credentials
提供商仅在开发环境中添加。在进行配置调整后,我们可以像上面一样编写 @playwright/test
测试。
tests/e2e/basic-auth.spec.ts
import { test, expect, type Page } from "@playwright/test"
test("Basic auth", async ({ page, browser }) => {
if (!process.env.TEST_PASSWORD) throw new TypeError("Missing TEST_PASSWORD")
await test.step("should login", async () => {
await page.goto("https://127.0.0.1:3000/auth/signin")
await page.getByLabel("Password").fill(process.env.TEST_PASSWORD)
await page.getByRole("button", { name: "Sign In" }).click()
await page.waitForURL("https://127.0.0.1:3000")
const session = await page.locator("pre").textContent()
expect(JSON.parse(session ?? "{}")).toEqual({
user: {
email: "[email protected]",
name: "Bob Alice",
image: "https://avatars.githubusercontent.com/u/67470890?s=200&v=4",
},
expires: expect.any(String),
})
})
await test.step("should logout", async () => {
await page.getByText("Sign out").click()
await page
.locator("header")
.getByRole("button", { name: "Sign in", exact: true })
.waitFor()
await page.goto("https://127.0.0.1:3000/auth/session")
expect(await page.locator("html").textContent()).toBe("null")
})
})