node.jshapi.jsnock

Does async/await handle uncaught rejection errors differently?


In the following code block (example #1), expect(this.req.headers["user-agent"]).to.equal("BOOM") throws error and test fails.

describe("http.get with headers", () => {
  it("should return response with status code 200", async () => {
    const userAgent =
      "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:74.0) Gecko/20100101 Firefox/74.0"
    nock("https://api.example.net")
      .get("/hello")
      .reply(function() {
        expect(this.req.headers["user-agent"]).to.equal("BOOM")
        return [200]
      })
    const httpInstance = new http({
      headers: {
        "user-agent": userAgent,
      },
    })
    let response = await httpInstance.get("https://api.example.net/hello")
    expect(response.statusCode).to.equal(200)
  })
})

In the following code block (example #2), expect(requestBody.message).to.equal("BOOM") throws "silent" error (return [200] is never executed), but the test passes. Why?

describe("logger.captureMessage(message, callback)", () => {
  it("should send captured exception to sentry", () => {
    return new Promise((resolve, reject) => {
      nock("https://sentry.io")
        .post("/api/3926156/store/")
        .reply((uri, requestBody: any) => {
          expect(requestBody.message).to.equal("BOOM")
          return [200]
        })
      logger.captureMessage("foo", () => {
        resolve()
      })
    })
  })
})

Using catch and triggering reject works, but why is this necessary when everything works fine in example #1?

describe("logger.captureMessage(message, callback)", () => {
  it("should send captured exception to sentry", () => {
    return new Promise((resolve, reject) => {
      nock("https://sentry.io")
        .post("/api/3926156/store/")
        .reply((uri, requestBody: any) => {
          try {
            expect(requestBody.message).to.equal("BOOM")
            return [200]
          } catch (error) {
            reject(error)
          }
        })
      logger.captureMessage("foo", () => {
        resolve()
      })
    })
  })
})

Solution

  • Making assertions inside Nock reply callbacks is not recommended. There are a lot of layers between where that function gets executed and your test runner. Most notably, the client making the request. Usually clients don't handle non-request errors well and end up concealing or even glossing over the error.

    There isn't one "correct" way to do this. By the looks of your second test, I'd recommend using a test level variable to store the body of the request. Then do the asserting in the captureMessage callback.

    Another note is that you're not correctly handing bubbling errors in the Promise of your second test. This line: return new Promise((resolve, reject) => { doesn't use an async keyword, which means you must call reject manually or any bubbling error will be lost in the ethos.