我在网上拿了一堆例子和 ChatGPT 代码来进行测试。我最终在参数
mockResolvedValue
下出现了一些红色波浪线,并出现错误:
Argument of type '{ email: string; }' is not assignable to parameter of type 'never'.ts(2345)
Argument of type 'string' is not assignable to parameter of type 'never'.ts(2345)
对于这两行:
(getUserByEmail as jest.Mock).mockResolvedValue({ email: '[email protected]' });
(bcrypt.hash as jest.MockedFunction<typeof bcrypt.hash>).mockResolvedValue('hashedPassword');
我的测试代码:
import React from "react";
import test from "node:test";
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { checkNumberOfUsers, createNewUser, updateProfile } from "../domain/userControl";
import { createUser, getNumberOfUsers, getUserByEmail } from "../data/local/userRepo";
import { newUserSchema } from "@/schema/custom";
import bcrypt from 'bcryptjs';
jest.mock("@/data/local/userRepo", () => ({
getNumberOfUsers: jest.fn(),
getUserByEmail: jest.fn(),
createUser: jest.fn(),
}));
jest.mock('bcryptjs', () => ({
hash: jest.fn(),
}));
// describe("checkNumberOfUsers", () => {
// beforeEach(() => {})
// });
jest.mock("@/data/local/userRepo", () => ({
getNumberOfUsers: jest.fn(),
getUserByEmail: jest.fn(),
createUser: jest.fn(),
}));
jest.mock('bcryptjs', () => ({
hash: jest.fn(),
compare: jest.fn(),
}));
describe('createNewUser', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("should call getNumberOfUsers", async () => {
await checkNumberOfUsers();
expect(getNumberOfUsers).toHaveBeenCalled();
});
it('returns error when fields are invalid', async () => {
const invalidValues = {
email: 'invalid-email',
password: 'short',
confirmPassword: 'short',
};
const result = await createNewUser(invalidValues);
expect(result).toEqual({ error: 'Invalid fields!' });
});
it('returns error when passwords do not match', async () => {
const values = {
email: '[email protected]',
password: 'password123',
confirmPassword: 'password124',
};
const result = await createNewUser(values);
expect(result).toEqual({ error: 'Passwords do not match!' });
});
it('returns error when email is already in use', async () => {
const values = {
email: '[email protected]',
password: 'password123',
confirmPassword: 'password123',
};
(getUserByEmail as jest.Mock).mockResolvedValue({ email: '[email protected]' });
const result = await createNewUser(values);
expect(getUserByEmail).toHaveBeenCalledWith(values.email);
expect(result).toEqual({ error: 'Email already in use!' });
});
it('creates a new user successfully', async () => {
const values = {
email: '[email protected]',
password: 'password123',
confirmPassword: 'password123',
};
(getUserByEmail as jest.MockedFunction<typeof getUserByEmail>).mockResolvedValue(null);
(bcrypt.hash as jest.MockedFunction<typeof bcrypt.hash>).mockResolvedValue('hashedPassword');
(createUser as jest.MockedFunction<typeof createUser>).mockResolvedValue({ id: '1', email: '[email protected]', password: 'hashedPassword' });
const result = await createNewUser(values);
expect(getUserByEmail).toHaveBeenCalledWith(values.email);
expect(bcrypt.hash).toHaveBeenCalledWith(values.password, 10);
expect(createUser).toHaveBeenCalledWith({
email: values.email,
password: 'hashedPassword',
});
expect(result).toEqual({ success: 'User created.' });
});
});
我要测试的代码:
"use server";
import {createUser, getNumberOfUsers, getUserByEmail, updateUser} from "@/data/local/userRepo";
import {newUserSchema, updateProfileSchema} from "@/schema/custom";
import {z} from "zod";
import bcrypt from "bcryptjs";
/**
* Updates the user profile with the provided values.
*
* @param values - The values to update the profile with.
* @returns An object with an error property if there was an error during the update, otherwise undefined.
*/
export async function updateProfile(values: z.infer<typeof updateProfileSchema>) {
const validation = updateProfileSchema.safeParse(values)
if (!validation.success) {
return {error: "Invalid input"}
}
const {email, originalPassword, password} = validation.data
const existingUser = await getUserByEmail(email)
if (!existingUser) {
return {error: "User not found"}
}
const passwordMatch = await bcrypt.compare(originalPassword, existingUser.password)
if (!passwordMatch) {
return {error: "Incorrect password"}
}
const hashedPassword = await bcrypt.hash(password, 10)
await updateUser(existingUser.id, {email, password: hashedPassword})
}
export async function checkNumberOfUsers() {
return await getNumberOfUsers()
}
export const createNewUser = async (values: z.infer<typeof newUserSchema>) => {
const validatedFields = newUserSchema.safeParse(values);
if (!validatedFields.success) {
return {error: "Invalid fields!"};
}
const {email, password, confirmPassword} = validatedFields.data;
if (password !== confirmPassword) {
return {error: "Passwords do not match!"};
}
const hashedPassword = await bcrypt.hash(password, 10);
const existingUser = await getUserByEmail(email);
if (existingUser) {
return {error: "Email already in use!"};
}
await createUser({
email: email,
password: hashedPassword,
});
// Add any other business logic here (e.g., checking credentials)
return {success: "User created."};
};
我还尝试将
getUserByEmail
和 bcrypt.hash
更改为:
getUserByEmail.mockResolvedValue({ email: '[email protected]' });
bcrypt.hash.mockResolvedValue('hashedPassword');
以下类型为“openURL”的参数不能使用 Jest 分配给类型为“never”的参数,但我得到:
Property 'mockResolvedValue' does not exist on type '(email: string) => Promise<{ id: string; email: string; password: string; } | null>'.ts(2339)
和:
Property 'mockResolvedValue' does not exist on type '{ (s: string, salt: string | number): Promise<string>; (s: string, salt: string | number, callback?: ((err: Error | null, hash: string) => void) | undefined, progressCallback?: ((percent: number) => void) | undefined): void; }'.ts(2339)
深入研究,我发现您可以将mockreturnvalueonce用于“非异步函数”。虽然我不知道为什么会修复它并且我的测试用例通过了,因为我不知道 bcrypt 是否是异步函数。
(getUserByEmail as jest.Mock).mockReturnValueOnce({ email: '[email protected]' });
(bcrypt.hash as jest.MockedFunction<typeof bcrypt.hash>).mockReturnValueOnce();