mrkeyoor.com_
Sat 08 Aug 22:49 UTC
npmTestingupdated 08 Aug 2026

should

should.js is a framework-independent assertion library for JavaScript tests. It turns checks into readable chains such as user.should.have.property('name'), and covers deep equality, types, numbers, strings, containment, thrown errors, and promises. Its signature feature is a non-enumerable should getter on Object.prototype, though the package also provides a function-only entry point that avoids changing globals. The API is broad and still heavily downloaded, but the published release and most recent source work are years old.

Verdict

Keep should.js where an established suite already depends on its fluent chains. Do not choose an archived, 2018 package for a new test stack when Chai, expect, and built-in runner assertions have active ecosystems and fewer global-side-effect concerns.

API stability4/5The 13.x assertion surface has effectively frozen: equal, eql, property, containDeep, match, throw, and promise helpers have not been churned by recent releases. That makes an old suite predictable, and the README still documents a noConflict escape hatch plus the side-effect-free should/as-function entry. The score stops short of five because frozen code is not the same as compatibility work, especially as JavaScript runtimes and module systems continue changing around it.
Docs3/5The README explains the Object.prototype getter honestly, gives the function-only alternative, documents chaining behavior, and links API pages, upgrade notes, examples, and browser caveats. Source files contain useful examples beside each assertion. However, the hosted documentation was last modified in 2017, the README still recommends Bower for browsers, and modern ESM or current TypeScript usage is not addressed, so readers must translate older instructions into today's tooling.
Maintenance1/5GitHub marks shouldjs/should.js archived. Version 13.2.3 was published in July 2018, the last non-merge code change shown in the repository arrived in March 2019, and the last push was in October 2019. Eleven open items include issues and pull requests dating back years, including promise behavior and browser testing concerns. High download volume does not change the absence of releases, triage, runtime compatibility updates, or security maintenance.
Ecosystem3/5The package recorded 4,912,674 downloads for the measured week and has 1,891 GitHub stars, so it remains embedded in a large amount of existing JavaScript software. The README lists adapters for Sinon, Immutable.js, HTTP responses, Karma, jq, and spies. Most of that surrounding ecosystem is also from the same era, though, and new test tooling tends to document Chai, Jest-style expect, or built-in Node assertions first rather than should.js plugins.

Use it if

  • You maintain an existing test suite already written with should.js chains and want to avoid a large assertion rewrite
  • You prefer readable BDD-style assertions but need them to work with Mocha or another test runner rather than a bundled runner
  • You need one assertion package that includes deep containment, promise assertions, numeric ranges, and custom assertion extensions
  • You can use the should/as-function entry point consistently to avoid modifying Object.prototype
Skip it if

Setup reality

Installation is only npm install --save-dev should, with no native build, peer dependency, credential, or configuration file. The important choice comes before the first assertion. require('should') mutates Object.prototype by adding a non-enumerable should getter; this is intentional, but it can violate lint rules, surprise code that works with unusual prototypes, and does not work on values created with Object.create(null). Use require('should/as-function') when global prototype changes are unacceptable, then write should(value) everywhere. The package is CommonJS-oriented. Its README shows import * as should from 'should' for TypeScript, and it ships should.d.ts, but that declaration surface comes from the 2018 release rather than a current TypeScript toolchain. Promise assertions return promises, so a test must return or await the chain or the runner can finish before failure arrives. Browser use is a separate old path: the README tells you to use the repository's should.js bundle or build one and refers to Bower plus a known-bugs page. There is no maintained ESM/browser export story. Finally, property() and length() move the active assertion object to the selected value, while filler words such as be, have, and which do nothing. Long chains can therefore assert against a different object than a reader expects unless they are kept short.

Patterns

Use should.js without changing Object.prototypeavoid-prototype-extension

const should = require('should/as-function');

should(result).have.property('status', 'ok');
should(null).not.be.ok();

The default require('should') installs a getter on Object.prototype. The as-function entry point does not attempt that modification and also works for null and Object.create(null) values.

Check strict equalityassert-strict-equality

const should = require('should/as-function');

should(answer).equal(42);
should(label).exactly('ready');

equal, equals, and exactly are aliases for strict equality. Use eql for structural comparison instead.

Compare objects and arrays deeplyassert-deep-equality

should(actual).eql({
  id: 7,
  roles: ['editor', 'reviewer'],
});

eql is the deep comparison assertion; equal compares object identity and will fail for separately created objects with the same fields.

Check property names and valuesassert-properties

should(user)
  .have.property('profile')
  .which.have.properties({ name: 'Ada', active: true });

property changes the current assertion object to that property's value. Assertions later in the same chain run against profile, not the original user.

Check types and instancesassert-types

should(items).be.an.Array();
should(total).be.a.Number();
should(createdAt).be.instanceOf(Date);
should(maybeValue).not.be.undefined();

Capitalized helpers such as Array and Number are should.js methods, while instanceOf is the constructor check.

Check deep partial containmentassert-containment

should(response).containDeep({
  user: { roles: ['admin'] },
});
should(['a', { id: 2 }]).containEql({ id: 2 });

containDeep allows nested partial matches and unordered array containment. Use containDeepOrdered when array order is part of the contract.

Check string prefixes and patternsassert-string-pattern

should(filename).startWith('report-').and.endWith('.csv');
should(email).match(/^[^@]+@example\.com$/);

match also accepts objects and functions, which can make its behavior less obvious. A regular expression is the clearest form for strings.

Check ranges and approximate numbersassert-number-range

should(statusCode).be.within(200, 299);
should(ratio).be.approximately(0.3, 0.001);
should(count).be.aboveOrEqual(1);

approximately takes an absolute delta, not a percentage tolerance. within includes both boundary values.

Check a synchronous thrown errorassert-thrown-error

should(() => parseConfig('bad')).throw(Error, {
  message: /invalid config/i,
});

throw only observes synchronous exceptions. Use rejectedWith for a function that returns a rejected promise.

Check a rejected promiseassert-promise-rejection

await should(loadMissingUser()).be.rejectedWith(Error, {
  message: /not found/i,
});

Return or await the assertion. If the promise chain is left floating, the test runner can complete before should.js reports the failure.

Assert the eventual resolved valueassert-promise-result

await should(fetchCount()).eventually.be.a.Number().and.above(0);
await should(Promise.resolve({ ok: true })).be.fulfilledWith({ ok: true });

eventually unwraps the fulfilled value for the remaining chain. fulfilledWith uses deep equality for the expected value.

Register a project-specific assertionadd-custom-assertion

const should = require('should/as-function');

should.Assertion.add('validUser', function () {
  this.params = { operator: 'to be a valid user' };
  should(this.obj).have.properties('id', 'email');
  should(this.obj.id).be.a.Number();
});

should(user).be.a.validUser();

Set this.params before checking the value so should.js can build a useful outer failure message. Custom assertions are global within the loaded should.js instance.

Alternatives

PackageRegistryPick it when
chainpmYou want a widely used BDD assertion API with ongoing releases and plugin support
expectnpmYou prefer Jest-style expect(value) assertions without installing a full test runner
power-assertnpmYou want standard assert expressions with richer failure output and accept a transform step