使用Puppeteer捕获HTTP响应的正确方法是什么?

时间:2017-11-01 02:32:26

标签: google-chrome integration-testing puppeteer

我正在尝试从用户注册中捕获http响应状态。

我的代码如下所示:

  it.only('returns a 400 response if email is taken', async () => {
    await page.goto(`${process.env.DOMAIN}/sign-up`)
    await page.waitFor('input[id="Full Name"]')

    await page.type('input[id="Full Name"]', 'Luke Skywalker')
    await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com')
    await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})
    await page.click('input[type="submit"]', {delay: 1000})

    const response = await page.on('response', response => response)

    console.log('request status', response.status)
    // expect(response).toEqual(400)
  })

文档举例说明拦截请求并用它做事:

await page.setRequestInterception(true);
page.on('request', request => {
  request.respond({
    status: 404,
    contentType: 'text/plain',
    body: 'Not Found!'
  });
});

我尝试了类似的模式无济于事,以及许多其他模式。我所做的一切都会返回page,这是一个巨大的物体,没有任何状态,我可以看到。任何帮助深表感谢。

工作原理:

感谢@tomahaug指导我正确的方向。我的第一个问题是放置,需要在发出请求之前设置监听器,我在请求之后就有了它。说得通。我最大的问题是将监听器分配给变量,以便我可以将expect作为我的最后一行。将其分配给变量会导致返回page。我需要做的只是在监听器内运行测试。在使用done()投掷和错误时,我关闭了我的测试,如下所示,我的代码的工作版本:

it.only('returns a 400 response if email is taken', async () => {
    await page.goto(`${process.env.DOMAIN}/sign-up`)
    await page.waitFor('input[id="Full Name"]')

    await page.type('input[id="Full Name"]', 'Luke Skywalker')
    await page.type('input[id="Email"]', 'LukeSkywalker@voyage1.com')
    await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})

    await page.on('response', response => {
      if (response.request().method === 'POST' && response.url === `${process.env.USERS_API_DOMAIN}/sessions`) {
        expect(response.status).toEqual(400)
      }
    })

    await page.click('input[type="submit"]', {delay: 1000})
  })

  after(async function () {
    await browser.close()
  })

希望这有助于其他人!

4 个答案:

答案 0 :(得分:5)

我相信你应该按照这些方针做点什么。请注意回调函数done

代码的作用是,它附加一个响应的监听器,然后单击提交按钮。收到响应后,它会检查状态代码,断言它,并通过调用done来终止测试。

您可能希望有一个if - 语句,检查它是您在回调中检查的表单的实际响应,因为响应处理程序可能会为其他并发请求发出事件。

it.only('returns a 400 response if email is taken', () => {
  await page.goto(`${process.env.DOMAIN}/sign-up`)
  await page.waitFor('input[id="Full Name"]')

  await page.type('input[id="Full Name"]', 'Luke Skywalker')
  await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com')
  await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100})

  await page.on('response', (response) => {
    if (
      response.request().method === 'POST' && 
      response.url === `${process.env.USERS_API_DOMAIN}/sessions`) 
    {
      expect(response.status).toEqual(400)
    }
  })

  await page.click('input[type="submit"]', {delay: 1000})
})

我没有测试过代码,但它应该给你正确的想法。

编辑:调整以反映最终的结果。

答案 1 :(得分:2)

如果您需要操纵请求/响应,请使用page.setRequestInterception(true)page.on / page.onceas documented)。

但是,如果您只需要声明有关响应的内容,那么最简单,最惯用的方法是使用page.waitForResponse

const updateDashboardResponse = await page.waitForResponse(response =>
  response.url().includes('updateDashboard')
);
expect(updateDashboardResponse.status()).toBe(200);

这使测试流程保持线性,并避免在page.on处理程序接收到response事件之前关闭测试的不确定性。

答案 2 :(得分:0)

response.url是一个函数,您必须调用它:

response.url()

response.request().method也是如此:

response.request().method()

答案 3 :(得分:0)

接受的答案(也已编辑到问题中)不正确。由于将1秒钟的延迟添加到点击通话中,因此引入了竞争条件。最好的情况是,这不必要地减慢了测试套件的速度,最坏的情况是,如果请求花费的时间超过一秒钟才能解决,则它会生成错误的失败(如果被嘲笑是不太可能的,但它不会改变代码不安全的事实)。 / p>

只要在Jest测试用例中存在回调,便会确保call done() from the callback的正确方法是确保其已被执行,并且根据其触发进行了所有断言,而没有添加人为延迟。如果在回调中抛出异常,导致done无法访问,请在错误处理程序中调用done(error),以将测试用例失败报告给Jest。

为此,您需要将done作为参数添加到传递给ittestonly函数的回调中,以便在块。这允许Jest的测试运行程序将测试视为异步,并且直到调用done才解决它。如果没有done,则测试套件将忽略回调的断言。 async / await没有帮助,因为它是与回调不同的异步链。

您只需要指定done作为参数或返回一个承诺(async隐式返回一个承诺),而不必两者都做。但是,您仍可能希望对await使用then来进行Puppeteer库调用。您可以使用async IIFE,当所有断言都激发出来时,它最终会激发done()调用。

例如,

it.only('returns a 400 response if email is taken', done => {
  (async () => {
    page.on('response', response => {
      if (response.request().method === 'POST' && 
          response.url === `${process.env.USERS_API_DOMAIN}/sessions`) {
        try {
          expect(response.status).toEqual(400);
          done();
        } 
        catch (err) {
          done(err);
        }
      }
    });    
    await page.goto(`${process.env.DOMAIN}/sign-up`);
    await page.waitFor('input[id="Full Name"]');
    await page.type('input[id="Full Name"]', 'Luke Skywalker');
    await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com');
    await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100});
    await page.click('input[type="submit"]');
  })();
});

考虑到这一点,this answer显示了一种使用waitForResponse的更好的方法,它使您可以完全跳过回调和donewaitForResponse的回调是一个字符串URL或函数谓词,对于正在等待的目标响应应返回true:

it.only('returns a 400 response if email is taken', async () => {
  await page.goto(`${process.env.DOMAIN}/sign-up`);
  await page.waitFor('input[id="Full Name"]');
  await page.type('input[id="Full Name"]', 'Luke Skywalker');
  await page.type('input[id="Email"]', 'LukeSkywalker@voyage.com');
  await page.type('input[id="Password"]', 'LukeSkywalker123', {delay: 100});
  await page.click('input[type="submit"]');
  const response = await page.waitForResponse(response =>
    response.request().method === 'POST' && 
    response.url === `${process.env.USERS_API_DOMAIN}/sessions`
  );
  expect(response.status).toEqual(400);
});

我还应该提到,以上摘录中的waitFor已被waitForSelector取代,并且.url.method是函数。我尚未验证以上代码;它与原始帖子相关并显示高级模式。


最小的完整示例

index.html

这是我们正在测试的网页。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <button>Post</button>
    <script>
      document
        .querySelector("button")
        .addEventListener("click", e =>
          fetch("https://jsonplaceholder.typicode.com/posts", {
              method: "POST",
              body: JSON.stringify({
                title: "foo",
                body: "bar",
                userId: 1,
              }),
              headers: {
                "Content-type": "application/json; charset=UTF-8",
              },
            })
            .then(response => response.json())
            .then(json => console.log(json))
        )
      ;
    </script>
  </body>
</html>

index.test.jsasync / await版本):

describe("index page", () => {
  it("should respond to POST", async () => {
    const url = "https://jsonplaceholder.typicode.com/posts";
    await page.goto("http://localhost:1234", {waitUntil: "load"});
    await page.click("button");
    const response = await page.waitForResponse(response =>
      response.request().method() === "POST" &&
      response.url() === url
    );
    const expectedBody = {
      body: "bar",
      id: 101,
      title: "foo",
      userId: 1,
    };
    expect(await response.json()).toEqual(expectedBody);
  });
});

index.test.jsthen版本):

describe("index page", () => {
  it("should respond to POST", () => {
    const url = "https://jsonplaceholder.typicode.com/posts";
    const expectedBody = {
      body: "bar",
      id: 101,
      title: "foo",
      userId: 1,
    };
    return page.goto("http://localhost:1234", {
        waitUntil: "load"
      })
      .then(() => page.click("button"))
      .then(() => page.waitForResponse(response =>
        response.request().method() === "POST" &&
        response.url() === url
      ))
      .then(response => response.json())
      .then(body => expect(body).toEqual(expectedBody))
    ;
  });
});

index.test.jsdone版本):

describe("index page", () => {
  it("should respond to POST", done => {
    (async () => {
      const url = "https://jsonplaceholder.typicode.com/posts";
      const expectedBody = {
        body: "bar",
        id: 101,
        title: "foo",
        userId: 1,
      };
      await page.setRequestInterception(true);
      page.on("response", async response => {
        if (response.request().method() === "POST" &&
            response.url() === url) {
          try {
            const body = await response.json();
            expect(body).toEqual(expectedBody);
            done();
          }
          catch (err) {
            done(err);
          }
        }
      });
      await page.goto("http://localhost:1234", {
        waitUntil: "load"
      });
      page.click("button");
    })();
  });
});