You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为Jest创建自定义触发机制,确保未注册URL的请求触发测试失败并复用MockClient?

如何为Jest创建自定义触发机制,确保未注册URL的请求触发测试失败并复用MockClient?

我明白你的问题了——你希望MockClient在遇到未注册的URL请求时,不管被测试类(比如MyClass)有没有用try-catch捕获异常,都能让测试稳稳失败,而且这个MockClient还能方便复用。咱们一步步来解决这个问题:

首先分析你原来的代码为什么没生效:

  1. 测试没等待异步操作完成:你的测试代码里testObject.load()是异步函数,但你没加await,导致Jest会在异步请求处理完之前就结束测试,完全没机会触发断言失败。
  2. 断言错误被被测试类的catch吃掉了:就算你加了await,MyClass的load方法里用try-catch捕获了jsonFetch的异常,包括Jest的expect抛出的断言错误,这会让load()的Promise最终resolve,Jest就认为测试通过了。

那怎么解决?核心思路是绕开被测试类的异常捕获,在测试生命周期的最后统一验证所有请求都已注册,这样不管被测试类怎么处理异常,测试结束时都会检查。


改进后的MockClient实现

我们给MockClient加一个未处理请求的记录列表,再补充注册请求的方法,最后在测试结束时统一检查:

// 先定义URLParameters类型(如果之前没定义的话)
type URLParameters = {
  method?: string;
  // 可补充你需要的其他参数,比如headers、body等
};

export class MockClient {
  // 存储已注册的请求处理器
  private registeredFetchers = new Map<string, { jsonFetch: (url: string, options?: URLParameters) => Promise<any> }>();
  // 记录所有未匹配的请求
  private unhandledRequests: Array<{ method: string; url: string; options?: URLParameters }> = [];

  // 注册请求处理器:method(比如get/post)、url、对应的mock实现
  registerFetcher(method: string, url: string, fetcher: { jsonFetch: (url: string, options?: URLParameters) => Promise<any> }) {
    // 用小写method+url作为唯一key,避免大小写匹配问题
    const key = `${method.toLowerCase()}:${url}`;
    this.registeredFetchers.set(key, fetcher);
  }

  private getFetcher(method: string, url: string) {
    const key = `${method.toLowerCase()}:${url}`;
    return this.registeredFetchers.get(key);
  }

  jsonFetch = async (url: string, options?: URLParameters) => {
    const method = options?.method?.toLowerCase() || "get";
    const fetcher = this.getFetcher(method, url);

    if (!fetcher) {
      // 记录未匹配的请求
      this.unhandledRequests.push({ method, url, options });
      // 抛出明确错误(可选,被测试类捕获也没关系,我们后面会统一检查)
      throw new Error(`未注册的请求:${method.toUpperCase()} ${url}`);
    }

    // 执行已注册的mock逻辑
    return await fetcher.jsonFetch(url, options);
  };

  // 验证所有请求都已被处理,测试结束时调用
  assertNoUnhandledRequests() {
    if (this.unhandledRequests.length > 0) {
      // 格式化未处理请求的信息,方便排查
      const requestDetails = this.unhandledRequests
        .map(req => `${req.method.toUpperCase()} ${req.url}`)
        .join('\n  - ');
      // 调用Jest的fail函数,强制标记测试失败
      fail(`发现未注册的请求:\n  - ${requestDetails}`);
      // 清空列表,避免影响下一个测试用例
      this.unhandledRequests = [];
    }
  }
}

正确的测试使用方式

现在,你的测试代码需要做两个关键修改:

  1. await所有异步操作,确保Jest等待所有请求处理完成。
  2. 在每个测试结束后调用assertNoUnhandledRequests(),检查有没有未注册的请求。
class MyClass {
  env: any;
  constructor(env: any) {
    this.env = env;
  }
  async load() {
    try {
      await this.env?.jsonFetch("badurl"); // 这个URL没注册,会触发测试失败
    } catch (error) {
      console.log(error); // 就算这里捕获错误,测试还是会失败
    }
  }
}

describe("testing quick access store", () => {
  const client = new MockClient();
  let env: Record<string, any>;

  beforeAll(() => {
    env = {
      jsonFetch: client.jsonFetch,
    };
  });

  // 每个测试结束后检查未处理请求
  afterEach(() => {
    client.assertNoUnhandledRequests();
  });

  test("请求未注册URL时测试应该失败", async () => {
    const testObject = new MyClass(env);
    // 必须await,确保异步请求完成,未处理请求被记录
    await testObject.load();
  });

  test("请求已注册URL时测试应该通过", async () => {
    // 先注册合法的请求
    client.registerFetcher("get", "/proper-url/", {
      async jsonFetch(url, options) {
        // 返回mock数据
        return { data: "hello world" };
      }
    });

    // 测试合法请求场景
    await env.jsonFetch("/proper-url/");
    // 可添加其他断言,比如验证返回数据是否符合预期等
  });
});

为什么这个方案有效?

  • 不受被测试类的异常捕获影响:不管MyClass怎么用try-catch捕获请求错误,我们都会在测试结束时统一检查未注册的请求,一旦有就标记测试失败。
  • 复用性强:MockClient的逻辑完全封装,所有测试用例都可以直接实例化使用,只需要注册对应URL的mock逻辑即可。
  • 排查友好:未注册请求的信息会被格式化输出,你能清楚看到是哪个方法、哪个URL没注册,方便快速定位问题。

额外注意点

  • 一定要await所有异步操作:如果测试里有未被await的异步函数,Jest会在操作完成前结束测试,导致未处理请求没被记录,检查就会失效。
  • 注册请求时注意method的大小写:我们在MockClient里统一转成小写,所以注册时用"GET"或"get"都可以,避免大小写不匹配的问题。

备注:内容来源于stack exchange,提问作者paul23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 12:59:50