mrkeyoor.com_
Wed 23 Sept 00:32 UTC
npmTestingupdated 22 Sept 2026

should review

should 13.2.3 is a test assertion library that adds fluent checks for equality, properties, types, numbers, strings, collections, thrown errors, and promises without choosing a test runner. Its default entry point installs a non-enumerable should getter on Object.prototype, producing chains such as user.should.have.property('id'). A separate should/as-function entry point keeps Object.prototype untouched and also works with null or Object.create(null) values. Our Node 22 install loaded through require() and ESM import, bundled TypeScript declarations, and brought 5 direct dependencies. The current version fixed .only.keys when the prototype getter is absent; it shipped in July 2018, and the GitHub repository is archived.

Verdict

should 13.2.3 installed in 1.7 seconds as 6 packages using 1 MB with 0 audit findings in our sandbox, but its release is from 2018 and its repository is archived. Keep it for an existing fluent suite; choose maintained assertions for new work and use should/as-function wherever prototype changes are off limits.

We installed it

Lab card: what happened when we installed shouldScreenshot of should documentation
Install✓ · 1.7s6 packages on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser11.6 KBgzipped (42.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does should install cleanly?

Yes. In a fresh container with an empty cache, npm install should finished in 2 seconds, leaving 6 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does should add to a browser bundle?

11.6 KB gzipped (42.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does should work with both ESM and CommonJS?

Yes. Both import 'should' and require('should') worked in Node 22 in our run. The package is published as CommonJS.

Does should include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

should or chai: which should you use?

chai: Use it for actively released BDD or TDD assertions and a current plugin ecosystem around common JavaScript test runners. should 13.2.3 installed in 1.7 seconds as 6 packages using 1 MB with 0 audit findings in our sandbox, but its release is from 2018 and its repository is archived.

When should you not use should?

You are choosing assertions for a new project. The repository is archived, 13.2.3 dates to July 2018, and the last GitHub push was in October 2019.

API stability4/5The 13.x assertion vocabulary is effectively frozen: exact and deep equality, property traversal, key checks, containment, numeric comparisons, type checks, thrown errors, promises, and custom Assertion.add hooks have not changed since 2018. Version 13.2.3 made one narrow correction to .only.keys in function-only use. Existing tests therefore face little package-driven churn. The missing point reflects runtime drift: frozen code receives no updates for new language objects, module resolution rules, or edge behavior in future Node releases.
Docs3/5The README explains the Object.prototype getter, should/as-function, noConflict(), null values, no-prototype objects, subject-changing chains, custom assertions, TypeScript loading, browser globals, and runner independence. Generated API pages remain online over HTTPS. The examples are detailed enough to maintain an old suite, but browser advice still includes Bower and a checked-in bundle, promise guidance predates modern async test conventions, and there is no maintained ESM or current TypeScript migration page.
Maintenance1/5npm published 13.2.3 on July 30, 2018. GitHub marks shouldjs/should.js archived, records its last push on October 29, 2019, and shows 11 open issues and pull requests combined. The final release fixed a real function-only .only.keys defect, but there have been no package releases, compatibility updates, or issue triage for years. A clean audit today does not provide a maintainer for the next runtime incompatibility or assertion bug.
Ecosystem3/5npm counted 5,133,387 downloads from August 18 through August 24, 2026, and GitHub reports 1,889 stars, evidence that should.js remains deep in older dependency trees and test suites. The README lists integrations for Sinon, Immutable.js, HTTP responses, Karma, jq, and spies. Most belong to the same testing era, while new framework documentation usually teaches Chai, Jest-style expect, Vitest matchers, or node:assert first. High installation volume here signals legacy reach more than current ecosystem investment.

Use it if

  • An established suite already uses should.js chains, custom assertions, or plugins, and rewriting failures would cost more than keeping the frozen dependency.
  • Tests run under Mocha or another runner and only need an assertion layer rather than a runner, mocking system, and coverage stack.
  • The team can standardize on should/as-function to avoid changing Object.prototype while retaining the existing assertion vocabulary.
  • Legacy code depends on should.js checks for Map, Set, promises, deep containment, or custom Assertion.add extensions.
Skip it if

Setup reality

Our clean Node 22 sandbox installed should 13.2.3 in 1.7 seconds. Six packages occupied 1 MB, while the should package itself is 428 KB unpacked. npm audit found 0 known vulnerabilities at critical, high, moderate, and low severity. It declares 5 direct dependencies and 0 peers, includes TypeScript declarations, and uses the MIT license. CommonJS require() and ESM import both worked in our measurements.

No native build, credentials, peer setup, or config file is required. The first import decides whether the process is modified. require('should') defines a non-enumerable should getter on Object.prototype. require('should/as-function') avoids that change and supports null plus objects with no prototype. Mixing the two entry points across test setup files makes behavior depend on load order, so choose one convention for the entire suite.

Fluent filler properties such as be, have, which, and and perform no check. property() and length() are different: they move the active assertion to the selected value. Keep chains short enough that the current subject is clear. Promise assertions return promises, so the test must await or return fulfilled(), rejected(), resolvedWith(), or rejectedWith(). A runner can otherwise finish before a late rejection reaches the assertion.

The package is CommonJS without an exports map, despite successful ESM interop on our Node 22 box. Its bundled types and browser instructions reflect the 2018 toolchain. Our esbuild browser test produced 42.4 KB minified and 11.6 KB gzipped. That is avoidable client weight for test code, and the repository's browser route relies on an old UMD bundle. Keep it in devDependencies and out of production bundles.

Patterns

Assert through the function-only entry avoid-prototype-change

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

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

should/as-function does not install the Object.prototype getter. It is also the usable form for null and Object.create(null) values.

Check and continue from a property assert-property

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

should(user)
  .have.property('profile')
  .which.have.property('name', 'Ada')

property('profile') changes the active assertion subject to user.profile. The next property check runs against that nested value.

Compare nested values compare-deeply

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

eql() performs value-oriented deep comparison. equal() and exactly() use strict identity semantics.

Require an exact key set check-exact-keys

should(payload).only.have.keys('id', 'name')

The only modifier makes keys() reject extra keys. Version 13.2.3 fixed this form when Object.prototype.should is absent.

Match part of a nested value check-deep-containment

should(response).containDeep({
  user: { roles: ['admin'] },
})

containDeep() checks nested partial content. Use eql() when extra fields or collection members should fail the assertion.

Check a measured tolerance assert-number-range

should(durationMs).be.within(95, 105)
should(ratio).be.approximately(0.5, 0.01)

within() includes the supplied bounds. approximately() compares against the stated delta.

Match a synchronous exception assert-thrown-error

should(() => parseConfig('bad')).throw(/invalid config/)

should(() => parseConfig(null)).throw(TypeError)

throw() invokes the function synchronously. Promise rejections need rejected() or rejectedWith() instead.

Await a rejected promise assert-rejected-promise

await should(loadUser(-1)).be.rejectedWith(/invalid id/)

rejectedWith() returns a promise. Await or return it so the test runner observes the final assertion result.

Check a fulfilled promise value assert-resolved-value

await should(Promise.resolve({ id: 7 })).be.fulfilledWith({ id: 7 })

fulfilledWith() performs the promised-value assertion asynchronously and returns a promise to the test.

Assert Set or Map size check-collection-size

should(new Set(['a', 'b'])).have.size(2)
should(new Map([['id', 7]])).have.key('id')

The 13.2 line supports Set as an iterable through its type-adaptor dependency. size() works across supported collection types.

Define a project assertion add-custom-assertion

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

should.Assertion.add('userId', function () {
  this.params = { operator: 'to be a user id' }
  should(this.obj).match(/^usr_[0-9]+$/)
})

should('usr_42').be.a.userId()

Assertion.add() expects the callback to set this.params before checking the positive case. should.js handles .not itself.

Remove the default global getter remove-global-getter

const should = require('should').noConflict()

should(Object.prototype).not.have.property('should')
should(value).be.ok()

noConflict() removes the getter installed by the default entry and returns the should function. Prefer should/as-function when no global change is wanted at all.

Alternatives

PackageRegistryPick it when
chainpmUse it for actively released BDD or TDD assertions and a current plugin ecosystem around common JavaScript test runners.
expectnpmUse it when Jest-style expect(value) matchers fit the suite and prototype modification is undesirable.
power-assertnpmUse it for richer output from ordinary assert expressions when an older transform-based workflow is already part of the project.

More testing guides

pytest · chai · jsdom · vitest · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.