deno_std: assertRejects does not fail when an error is thrown, but a promise was not rejected

Describe the bug

import { assertRejects } from "https://raw.githubusercontent.com/denoland/deno_std/main/testing/asserts.ts";
await assertRejects(function () { throw new Error("test") });

Expected behavior Should throw because no promise was rejected.

Node’s assertRejects behaviour:

> assert.rejects(function() { throw new Error("test"); }).catch(err => console.log(err));
> Error: test
    at REPL16:1:35
    at waitForActual (assert.js:786:21)
    at Function.rejects (assert.js:921:31)
    at REPL16:1:8
    at Script.runInThisContext (vm.js:134:12)
    at REPLServer.defaultEval (repl.js:486:29)
    at bound (domain.js:416:15)
    at REPLServer.runBound [as eval] (domain.js:427:12)
    at REPLServer.onLine (repl.js:819:10)
    at REPLServer.emit (events.js:387:35)

This might just be an issue with the renaming from assertThrowsAsync -> assertRejects and perhaps we should keep assertThrowsAsync then change assertRejects to assert that a promise is rejected?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 19 (19 by maintainers)

Commits related to this issue

Most upvoted comments

Okay my bad, I totally missed the point of synchronicity, which was the subject of this issue, and went in an insanely wrong direction. Doesn’t feel good to be dumb 😆

Sorry for (unintentionally) wasting your time.

View async functions as only rejecting a promise because that’s what they do.

Before changes :

  • assert that a function throws => use assertThrows
  • assert that an async function rejects/throws => use assertRejects
  • assert that a function that returns a promise throws synchronously => use assertThrows, but right now assertRejects incorrectly passes

After changes:

  • assert that a function throws => use assertThrows
  • assert that an async function rejects/throws => use assertRejects
  • assert that a function that returns a promise throws synchronously => use assertThrows and assertRejects should fail because it throws synchronously

With the change, how do we assert async functions that throw instead of returning a rejecting Promise? I wanted to test the new assertRejects implementation, but it is an async function that throws sweat

Async functions capture thrown errors and turn them into promise rejections. If the function wasn’t actually async and just returns a promise normally and it were to throw instead, the assertRejects assertion should fail because the function doesn’t reject, it throws.

assertThrows is not asynchronous, it only takes a non-async function as a parameter. I can make it async, but that would be another breaking change on top of this issue breaking change.

I think if assertThrows returns a rejected promise, it would fail because the function didn’t throw. I think that would be desired behavior.

Having this behavior would make it easier to catch and fix issues like errors being thrown incorrectly instead of being rejected and vice versa.

Here’s showing the critical difference between the two and something that will pass type checking:

function getFunc(throwsSync: boolean) {
  return () => {
    if (throwsSync) {
      throw new Error();
    }

    return Promise.reject(new Error());
  };
}

await assertRejects(getFunc(false)); // pass, ok
await assertRejects(getFunc(true)); // pass, not ok

// why this matters
myFunctionToTest(false).catch(err => { /* ok */ });
myFunctionToTest(false).catch(err => {}); // function throws even though assertRejects tests passed

A more common scenario of using this might be along the lines of:

const myInstance = new Something();

await assertRejects(() => myInstance.doAction());

I think we should…

  1. Change assertRejects to only pass when the function executed returns a promise that rejects.
  2. If the function throws and doesn’t return a promise that rejects then in addition to failing we should give a bit of an explanatory message to the user.
  3. In addition to a function, allow a promise as a parameter for assertRejects.