如何用Jest为Express上的GraphQL端点编写服务端验证测试用例
问题:用Jest批量测试服务端验证的输入组合
给定输入组合(已修正原代码语法错误):
const inputVariations =[ {email:"",password:""}, {email:"test",password:"232"}, {email:"test.testsite.com",password:"StrongPasswd234"} ];
API验证响应示例:
// 空输入对应的响应 { success : false, errors : [ {field:"email",message:"Email is required."}, {field:"password",message:"Password is required."}, ] } // 无效邮箱+弱密码对应的响应 { success : false, errors : [ {field:"email",message:"Enter a valid email address."}, {field:"password",message:"Password is weak."}, ] }
你原测试代码存在的核心问题:
forEach中使用异步函数时,Jest无法等待所有请求完成,会直接结束测试,导致断言不执行或失效- 断言逻辑宽泛,仅检查错误消息是否在数组中,未对应到具体输入的预期错误
- 用字符串模板拼接GraphQL查询,存在注入风险且可读性差
- 未处理请求失败的异常情况
优化后的测试方案
方案1:用test.each批量生成独立测试用例(推荐)
Jest的test.each可以为每个输入组合生成独立测试,同时明确绑定预期结果,便于定位问题:
// 定义输入与预期错误的映射关系 const testCases = [ // [输入数据, 预期错误列表] [ { email: "", password: "" }, [ { field: "email", message: "Email is required." }, { field: "password", message: "Password is required." } ] ], [ { email: "test", password: "232" }, [ { field: "email", message: "Enter a valid email address." }, { field: "password", message: "Password is weak." } ] ], [ { email: "test.testsite.com", password: "StrongPasswd234" }, [] // 若该输入合法,预期错误列表为空,success为true ] ]; test.each(testCases)('验证输入组合:email=%s, password=%s', async (input, expectedErrors) => { try { const result = await axios({ url: 'https://myapiendpoint/graphql', method: 'post', data: { // 用GraphQL变量代替字符串拼接,避免注入风险 query: ` mutation EmailRegister($input: ManageInput!) { emailRegister(manageInput: $input) { success errors { field message } } } `, variables: { input: { email: input.email, password: input.password } } } }); const { success, errors } = result.data.data.emailRegister; // 验证success状态:无预期错误则为true,否则为false expect(success).toBe(expectedErrors.length === 0); // 验证错误列表完全匹配(数量+内容) expect(errors).toEqual(expect.arrayContaining(expectedErrors)); expect(errors.length).toBe(expectedErrors.length); } catch (error) { // 捕获请求异常,避免测试静默失败 throw new Error(`请求失败: ${error.message}`); } });
方案2:用Promise.all在单个测试块中完成批量验证
如果需要在一个it块中执行所有测试,需用Promise.all确保Jest等待所有异步请求完成:
it('register validation for email and password', async () => { const inputVariations = [ { email: "", password: "" }, { email: "test", password: "232" }, { email: "test.testsite.com", password: "StrongPasswd234" } ]; // 映射每个输入对应的预期错误 const expectedErrorsMap = new Map([ [JSON.stringify(inputVariations[0]), [ { field: "email", message: "Email is required." }, { field: "password", message: "Password is required." } ]], [JSON.stringify(inputVariations[1]), [ { field: "email", message: "Enter a valid email address." }, { field: "password", message: "Password is weak." } ]], [JSON.stringify(inputVariations[2]), []] ]); // 用Promise.all等待所有请求完成 await Promise.all(inputVariations.map(async (input) => { const result = await axios({ url: 'https://myapiendpoint/graphql', method: 'post', data: { query: ` mutation EmailRegister($input: ManageInput!) { emailRegister(manageInput: $input) { success errors { field message } } } `, variables: { input } } }); const { success, errors } = result.data.data.emailRegister; const expectedErrors = expectedErrorsMap.get(JSON.stringify(input)); expect(success).toBe(expectedErrors.length === 0); expect(errors).toEqual(expect.arrayContaining(expectedErrors)); expect(errors.length).toBe(expectedErrors.length); })); });
关键优化说明
- GraphQL变量替代字符串拼接:避免注入风险,提升代码可读性和维护性
- 明确预期结果:每个输入绑定对应错误列表,断言更精准
- 异步等待处理:确保Jest等待所有异步操作完成,不会提前结束测试
- 异常捕获:添加try/catch捕获请求异常,避免测试静默失败
- 精确断言:不仅检查错误存在,还验证错误数量和内容完全匹配
内容的提问来源于stack exchange,提问作者Ajith
相关产品推荐
相关产品推荐

