Property-based Testing in JavaScript: Catching Bugs You Weren't Looking For

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

What if tests write themselves? An introduction to property-based testing

\n\n

Have you ever written a unit test, run it, gotten a green checkmark, and then a month later found a bug in the same function? Sound familiar? Classic unit tests are like checking that your wallet isn't empty by looking at just one bill. You check one specific scenario, one set of input data. But what if I told you there is a way to make the computer come up with thousands of different scenarios on its own and check if your code breaks?

\n\n

This isn't science fiction. This is property-based testing in JavaScript (or property-based testing). Instead of writing "if you pass 2 and 3, you get 5," you describe a property of your function: "for any two numbers, the result of addition will be a number that is always greater than or equal to each of the addends." And the test environment generates hundreds of random pairs of numbers, checking this assertion.

\n\n

In this article, we will break down how property-based testing works, how it differs from regular tests, and how to implement it in your project using the cool library fast-check. You will see how easy it is to find "edge case" bugs that you wouldn't even think to look for.

\n\n

Why aren't regular unit tests always enough?

\n\n

Let's get straight to the point. Suppose you have a function that sorts an array of numbers in ascending order:

\n\n
function sortNumbers(arr) {\n  return arr.slice().sort((a, b) => a - b);\n}
\n\n

You write a unit test:

\n\n
test('sorts the array [3, 1, 2]', () => {\n  expect(sortNumbers([3, 1, 2])).toEqual([1, 2, 3]);\n});
\n\n

The test is green. You are happy. But what if the array contains null? Or undefined? Or very large numbers? Or an array with 10,000 elements? You won't write a test for each of these cases. But property-based testing in JavaScript will.

\n\n

Instead of listing examples, you describe invariants (properties) that should always be true:

\n
    \n
  1. The length of the sorted array equals the length of the original array.
  2. \n
  3. Every element of the original array is present in the sorted array.
  4. \n
  5. For any index i, it is true that arr[i] <= arr[i+1].
  6. \n
\n\n

These are precisely the properties that property testing will check.

\n\n

The fast-check library: your helper in random testing

\n\n

In the JavaScript world, the main tool for property-based testing is fast-check. It can generate random data of any type: numbers, strings, arrays, objects, dates — and even complex structures.

\n\n

Installation is simple:

\n
npm install fast-check --save-dev
\n\n

Now let's write our first property-based test for the sortNumbers function. We'll use the Jest framework, but fast-check works with any test runner.

\n\n
const fc = require('fast-check');\n\ntest('sorting preserves the array length', () => {\n  fc.assert(\n    fc.property(fc.array(fc.integer()), (arr) => {\n      const sorted = sortNumbers(arr);\n      return sorted.length === arr.length;\n    })\n  );\n});
\n\n

What's happening here? fc.array(fc.integer()) is a generator that creates random arrays of random integers. fc.property takes this generator and checks the assertion (our arrow function) on 100 (by default) different sets of data. If even one test fails — fc.assert will throw an error and show the counterexample.

\n\n

Adding more properties

\n\n

One property is good, but three is better. Let's check that after sorting, the array is indeed ordered:

\n\n
test('sorting returns an ordered array', () =>                

Blogs

Book Recommendations