DEV Community

Kinjal Dixith
Kinjal Dixith

Posted on Originally published at kdreally.github.io

Unit testing a Node.js coupon route without Jest

The narration in these videos is synthesized. That is a convenience, so a finished script can be recorded without a studio session. The claim, the code, the order of the lectures, the corrections, and the decision to publish are human. The voice is not the author.

This is one finished course: seven short videos, one coupon program, Node's built-in test runner (node:test). No Jest. The claim is that making the program unit-testable forces the production code into a better shape. The tests are the evidence. The diff that matters is the production diff.

The seam

priceWithCoupon takes a subtotal, a coupon, and a time. It returns an error or a total. It does not open a database and it does not write an HTTP response. applyCoupon still does the I/O, but the database and the clock are arguments. A test can pass a fake database and a fixed date. Ten percent off 100, on a date when the coupon is valid, is 90. That assertion belongs to the price function and stays there.

Everything after that lecture is a new boundary, not a second check of the same 90.

What each lecture owns

  1. The price function. The rule is extracted from the route. The test fails on the old shape, where the database and the clock are hidden, and passes when they are arguments.
  2. The route, with a fake database. The test calls applyCoupon with a request body and a fake db. It asserts the status code and that the code SAVE10 was the one the route tried to load.
  3. SQLite. One real row. The assertion is still 90. The database is no longer a fake, and the price rule is unchanged.
  4. One inbound HTTP call. startCouponServer(db) listens on port 0. The test asserts that the coupon code arrived and that the status is 200. It does not assert the discount again.
  5. One outbound quote. shippingFor(total, quote) takes the quote as an argument. A total of 40 with a quote of 12 keeps 12. A total of 90 ships for 0. A failed quote is status 503, and the test does not call the network.
  6. Which test owns the failure. No new code. If 90 is wrong, the price test should fail. If the status is wrong, the route test should fail. If the quote is wrong, the shipping test should fail. A test that rechecks someone else's rule hides the owner.

The overview video names that map in a few minutes. The playlist is the course in order.

Where to watch, and where the code is

Start here if you want the map before the code: Unit Testing Node.js Without Jest: Course Overview.

The whole course, in order: Testing Node.js playlist.

The notes and the programs: github.com/kdreally/testing-node. The same notes, with the videos beside the code: kdreally.github.io/testing-node. Node 24, SQLite from node:sqlite, no packages to install. node --test runs the lectures.

The coupon program ends at lecture 6. A further lecture would need a hidden input this program does not have.

Top comments (0)