在单元测试中使用 asyncIterator 永远不会使用值进行解析

Using an asyncIterator inside a unit test never resolves with values

提问人:user686483 提问时间:11/10/2023 更新时间:11/15/2023 访问量:88

问:

我有一个来自 chatGPT 的可读流,我试图使用 asyncIterator 断言,然后读取这些值并断言它们。开玩笑测试运行良好,但我的逻辑中存在一个错误,导致它在每次迭代后都无法解决。

我错过了什么?

谢谢吉米

var util = require("util");

describe("stream", () => {
  it("should work", async () => {
    let i = -1;
    const completionStream: ChatCompletionStreamParams = {
      toReadableStream: () => {
        const mockReadable = {
          async *[Symbol.asyncIterator]() {
            const encoder = new util.TextEncoder("utf-8");
            yield encoder.encode(
              JSON.stringify({
                id: "1",
                object: "text",
                created: 1629159835,
                model: "gpt4",
                choices: [
                  {
                    index: 0,
                    delta: { content: "Hello" },
                    finish_reason: null,
                  },
                ],
              })
            ).buffer;
            yield encoder.encode(
              JSON.stringify({
                id: "1",
                object: "text",
                created: 1629159835,
                model: "gpt4",
                choices: [
                  {
                    index: 0,
                    delta: { content: "there" },
                    finish_reason: null,
                  },
                ],
              })
            ).buffer;
            yield null;
          },
          getReader: () => {
            return {
              read: async () => {
                const result = await mockReadable[
                  Symbol.asyncIterator
                ]().next();
                i++;
                if (i >= 2) {
                  return Promise.resolve({ done: true, value: null });
                }
                return Promise.resolve({ done: false, value: result?.value });
              },
            };
          },
        };
        return mockReadable;
      },
    };

    const readable = completionStream.toReadableStream().getReader();

    while (true) {
      const { done, value } = await readable.read();
      expect(done).toBe(false); // assert here
    }
  });
});
node.js typescript jestjs openai-api

评论

1赞 mandy8055 11/14/2023
我认为它正在无限期地运行。你是如何阻止它的?while(true)
0赞 user686483 11/15/2023
如果(完成)中断
0赞 user686483 11/15/2023
它永远不会到达那里,因为它一直在处理同一行。
0赞 mandy8055 11/15/2023
好的,让我提出一种根据我进行测试的方法。1.尝试创建一个计数器来跟踪迭代。(就在之前)。2. 检查 done 是否为 true。如果是这样,请打破循环。3. 增加计数器并执行您的断言(就像您正在做的那样)。之后,为每次迭代调用一个函数。

答:

2赞 hackape 11/15/2023 #1

你在零件上犯了一个错误。每次调用时都会调用它,从头开始创建一个新的异步迭代器,因此它会不断产生“Hello”,并且永远不会到达“那里”。getReadermockReadable[Symbol.asyncIterator]()readable.read()

您需要保存迭代器并重用它。

getReader: () => {
  const iterator = mockReadable[Symbol.asyncIterator]();
  return {
    read: async () => {
      const result = await iterator.next();
      return result;
    }

    // in fact you can just re-expose `iterator.next` as `read` like
    // read: iterator.next.bind(iterator),
  };
}

评论

0赞 user686483 11/21/2023
非常感谢您解释我做错了什么并提供解决方案。
-1赞 ali azam 11/15/2023 #2

它将工作文件:

it("should work", async () => {
  let i = -1;
  const completionStream: ChatCompletionStreamParams = {
    toReadableStream: () => {
      const mockReadable = {
        async *[Symbol.asyncIterator]() {
          const encoder = new util.TextEncoder("utf-8");
          yield encoder.encode(
            JSON.stringify({
              id: "1",
              object: "text",
              created: 1629159835,
              model: "gpt4",
              choices: [
                {
                  index: 0,
                  delta: { content: "Hello" },
                  finish_reason: null,
                },
              ],
            })
          ).buffer;
          yield encoder.encode(
            JSON.stringify({
              id: "1",
              object: "text",
              created: 1629159835,
              model: "gpt4",
              choices: [
                {
                  index: 0,
                  delta: { content: "there" },
                  finish_reason: null,
                },
              ],
            })
          ).buffer;
          yield null;
        },
        getReader: () => {
          return {
            read: async () => {
              const result = await mockReadable[
                Symbol.asyncIterator
              ]().next();
              i++;
              if (i >= 2) {
                return Promise.resolve({ done: true, value: null });
              }
              return Promise.resolve({ done: false, value: result?.value });
            },
          };
        },
      };
      return mockReadable;
    },
  };

  const readable = completionStream.toReadableStream().getReader();

  while (true) {
    const { done, value } = await readable.read();
    expect(done).toBe(false); // assert here

    if (done) {
      break; // exit the loop when done is true
    }
  }
});