• Stars
    star
    334
  • Rank 122,111 (Top 3 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 10 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

An assertion library for JavaScript and Node.js with a friendly BDD syntax (awesome.must.be.true()). It ships with many expressive matchers and is test runner and framework agnostic. Follows RFC 2119 with its use of MUST. Good stuff and well tested.

Must.js

NPM version Build status

Must.js is a testing and assertion library for JavaScript and Node.js with a friendly BDD syntax (awesome.must.be.true()). It ships with many expressive matchers and is test runner and framework agnostic. Follows RFC 2119 with its use of MUST. Good and well testsed stuff.

For those new to testing JavaScript on Node.js, you'll also need a test framework (also called a test-runner or a harness) to run your tests. One such tool is Mocha.

Tour

  • Assert with a beautiful and fluent chain that saves you from wrapping objects manually and reads nicely, too:

    require("must/register")
    obj.must.be.true()
  • Supports the expect flavor of wrapping as well:

    var demand = require("must")
    demand(obj).be.string()
  • Many expressive matchers out of the box, including:

    [].must.be.empty()
    obj.must.have.nonenumerable("foo")
    (42).must.be.above(13)
  • Simple, because matchers always behave the same way and don't depend on any "special flags" in the chain. They are also not interdependent the way foo.should.have.property(x).with.lengthOf(5) would be.

  • Reasonable, because it asserts only when you call the matcher [].must.be.empty() and not when you merely get the property empty. See below why asserting on property access is dangerous in other assertion libraries.

  • Has an intelligent and type-safe recursive eql matcher that compares arrays and objects by content and supports value objects. It's fully type-safe, so instances of different classes aren't eql, even if their properties are. It also supports circular and self-referential objects.

    primesBelowTen.must.eql([2, 3, 5, 7])
    model.attributes.must.eql({title: "New", createdAt: new Date(2000, 1, 1)})
  • Built-in support for asserting on promises with stack traces leading back to your assertion, not to the library's internals.

    Promise.resolve(42).must.then.equal(42)
    Promise.resolve([1, 2, 3]).must.eventually.not.include(42)
    Promise.reject(new Error("Problemo")).must.reject.with.error(/problem/i)
  • Human readable error messages let you know if an object wasn't what you expected. You can also customize or prepend to the autogenerated error message for further clarification.

  • Honors RFC 2119 by using the word MUST because your tests assert things, they don't list wishes or prayers, right? Exactly! Foo.must.equal(42), not foo.pretty.please.equal(42).

  • Works with any test runner and framework.

  • Avoids type coercions and mismatches.

  • Well tested — over 700 cases in over 2500 lines of tests. That makes a test to code ratio of 5:1.

Using Should.js or Chai.js? Switch for safety!

Among other things, one reason why Should.js and Chai.js inspired me to write Must.js is that they have a fundamental design mistake that makes them both surprising in a bad way and dangerous to use. Read more below.

Extensible

Must.js features a very simple implementation and one you can extend yourself. In Must.js, every matcher is a function on Must.prototype that calls Must.prototype.assert. For now, please see the source of Must for examples.

There are plugins for Must.js by others available, too.

Installing

Note: Must.js will follow the semantic versioning starting from v1.0.0.

Installing on Node.js

npm install must

Installing for the browser

Must.js doesn't yet have a build ready for the browser, but you might be able to use Browserify to have it run there till then.

Using

To use the fluent chain, just require Must.js's "register" file and it'll make itself available everywhere:

require("must/register")

Then just access the must property on any object and call matchers on it.

answer.must.equal(42)
new Date().must.be.an.instanceof(Date)

If you wish to use the expect flavor, assign Must to any name of your choice, e.g:

var expect = require("must")
var demand = require("must")

And call it with the object you wish to assert:

expect(answer).to.equal(42)
demand(null).be.null()

For a list of all matchers, please see the Must.js API Documentation.

Negative asserting or matching the opposite

To assert the opposite, just add not between the chain:

true.must.not.be.false()
[].must.not.be.empty()

Use it multiple times to create lots of fun puzzles! :-)

true.must.not.not.be.true()

Asserting on null and undefined values

In almost all cases you can freely call methods on any object in JavaScript. Except for null and undefined.

Most of the time this won't be a problem, because if you're asserting that something.must.be.true() and something ends up null, the test will still fail. If, however, you do need to assert its nullness, aliasing Must to expect or demand and wrapping it manually works well:

var demand = require("must")
demand(something).be.null()
demand(undefined).be.undefined()

If you've got an object on which a null or an undefined property must exist in addition to having a nully value, use the property matcher:

var obj = {id: null, name: undefined}
obj.must.have.property("id", null)
obj.must.have.property("name", undefined)

Autoloading

If your test runner supports an options file, you might want to require Must there so you wouldn't have to remember to require in each test file.

For Mocha, that file is test/mocha.opts:

--require must/register

Full example

Inside a test runner or framework things would look something like this:

require("must/register")
var MySong = require("../my_song")

describe("MySong", function() {
  it("must be creatable", function() {
    new MySong().must.be.an.instanceof(MySong)
  })

  it("must have cowbell", function() {
    new MySong().cowbell.must.be.true()
  })

  it("must not have pop", function() {
    new MySong().must.not.have.property("pop")
  })
})

API

For extended documentation on all functions, please see the Must.js API Documentation.

Must

Migrating to Must.js

You're likely to be already using some testing library and have a set of tests in them. I'm honored you picked Must.js to go forward. Let's get you up to speed on how Must.js differs from others and how to migrate your old tests over.

From Should.js

Must.js and Should.js are fairly similar when it comes to matchers.

  • Just add parentheses after each assertion and you're almost set.
  • Must.js does not have static matchers like should.not.exist(obj.foo).
    Convert to demand(foo).not.to.exist().
  • Must.js lacks with.lengthOf because its matchers are all independent.
    Convert to obj.must.have.length(5)
  • Must.js lacks the ok matcher because unambiguous names are better.
    Convert to truthy.
  • Must.js does not support custom error descriptions.

Here's a quick sed script to convert obj.should.xxx style to obj.must.xxx():

sed -i.should -E -f /dev/stdin test/**/*.js <<-end
  /\.should\.([[:alpha:].]+)([[:space:]}\);]|$)/s/\.should\.([[:alpha:].]+)/.must.\1()/g
  s/\.should\.([[:alpha:].]+)/.must.\1/g
end

From Chai.js

Must.js and Chai.js are fairly similar when it comes to matchers.

  • Just add parentheses after each assertion and you're almost set.
    That goes for both the BDD (obj.should) and expect (expect(obj).to) flavor.
  • Must.js lacks the include flag because its matchers are all independent.
    Convert to Object.keys(obj).must.include("foo").
  • Must.js lacks the deep flag for the equal matcher because eql already compares recursively and in a type-safe way.
    Convert to obj.must.eql({some: {deep: "object"}}).
  • Must.js lacks the deep flag for the property matcher because it prefers regular property access.
    Convert to obj.some.nested.property.must.equal(42).
  • Must.js lacks the ok matcher because unambiguous names are better.
    Convert to truthy.
  • Must.js lacks the respondTo matcher because unambiguous names are better.
    Convert to MyClass.prototype.must.be.a.function().

Here's a quick sed script to convert obj.should.xxx style to obj.must.xxx():

sed -i.should -E -f /dev/stdin test/**/*.js <<-end
  /\.should\.([[:alpha:].]+)([[:space:]}\);]|$)/s/\.should\.([[:alpha:].]+)/.must.\1()/g
  s/\.should\.([[:alpha:].]+)/.must.\1/g
end

Convert test case titles to MUST

If you've used the should style before, you most likely have test cases titled it("should do good").
Migrate those to it("must do good") with this sed script:

sed -i.should -E -e 's/it\("should/it("must/g' test/**/*.js

Beware of libraries that assert on property access

Among other things, one reason why Should.js and Chai.js inspired me to write Must.js is that they have a fundamental design mistake that makes them both surprising in a bad way and dangerous to use.

It has to do with them asserting on property access, like this:

true.should.be.true
[].should.be.empty

What initially may seem familiar to Ruby programmers, first of all, is out of place in JavaScript. Dot-something stands for getting a property's value and getters, regardless of language, should not have side-effects. Especially not control-flow changing exceptions!

Secondly, and this is where it's flat out dangerous asserting on property access, is that accessing a non-existent property does nothing in JavaScript. Recall that JavaScript does not have Ruby's method_missing or other hooks to catch such access. So, guess what happens when someone mistypes or mis-remembers a matcher? Yep, nothin' again. And that's the way it's supposed to be. But what's good in JavaScript, not so good for your now false positive test.

Imagine using a plugin that adds matchers for spies or mocks. Then using it with someFn.should.have.been.calledOnce. Someone accidentally removes the plugin or thinks calledQuadrice sounds good? Well, those assertions will surely continue passing because they'll now just get undefined back.

Must.js solves both problems with the simplest but effective solution — requires you to always call matchers because they're plain-old functions — expect(problem).to.not.exist().

Plugins

If you have a module extending Must.js one not listed above, please let me know or create a pull request.

License

Must.js is released under a Lesser GNU Affero General Public License, which in summary means:

  • You can use this program for no cost.
  • You can use this program for both personal and commercial reasons.
  • You do not have to share your own program's code which uses this program.
  • You have to share modifications (e.g bug-fixes) you've made to this program.

For more convoluted language, see the LICENSE file.

About

Andri Möll typed this and the code.
Monday Calendar supported the engineering work.
BrowserStack supports with cross-browser testing.

If you find Must.js needs improving, please don't hesitate to type to me now at [email protected] or create an issue online.

More Repositories

1

vim-node

Tools and environment to make Vim superb for developing with Node.js. Like Rails.vim for Node.
Ruby
808
star
2

vim-bbye

Delete buffers and close files in Vim without closing your windows or messing up your layout. Like Bclose.vim, but rewritten and well maintained.
Vim Script
637
star
3

node-mitm

Intercept and mock outgoing Node.js network TCP connections and HTTP requests for testing. Intercepts and gives you a Net.Socket, Http.IncomingMessage and Http.ServerResponse to test and respond with. Super useful when testing code that hits remote servers.
JavaScript
635
star
4

json-stringify-safe

Like JSON.stringify, but doesn't throw on circular references
JavaScript
543
star
5

capistrano-rsync

Deploy with Rsync from any local (or remote) repository when using Capistrano. Capistrano v3 ready!
Ruby
125
star
6

js-ddl

Gets you a JSON Schema from PostgreSQL or SQLite3. Also supports arrays and default values. Use it for introspection or preparing your domain models like with Rails's Active Record.
JavaScript
68
star
7

js-fetch-defaults

Fetch API mixin to set a default base URL and options. Functional and immutable.
JavaScript
55
star
8

mina-rsync

Deploy with Rsync from any local (or remote) repository when using Mina.
Ruby
50
star
9

js-concert

An event library for JavaScript and Node.js that implements the observer pattern (a.k.a publish/subscribe). Similar to Node's EventEmitter and Backbone.Events, but independent, minimal and light-weight.
JavaScript
43
star
10

js-standard-error

Tiny JavaScript library that simplifies subclassing and inheriting from Error while keeping the correct name and stack. Also supports constructing from an object of properties. Saves you from boilerplate.
JavaScript
41
star
11

js-undersign

A JavaScript library for creating eIDAS compatible XAdES signatures, incl. support for OCSP, timestamps and ASIC-E. Works also with the Estonian Id-card, Mobile-Id and Smart-Id out of the box.
JavaScript
29
star
12

js-strange

Range object for JavaScript. Supports exclusive and infinite ranges. Stringifies to PostgreSQL compatible format.
JavaScript
26
star
13

node-pg-error

An error class for Node.js that parses PostgreSQL's ErrorResponse format and sets human readable field names. Works with node-pg, too.
JavaScript
22
star
14

sh-chnode

Change between installed Node versions in your current shell.
Shell
22
star
15

js-promise-defer

JavaScript Polyfill for Promise.defer. Uses the native ES6 Promise. Supports other Promises/A+ implementations.
JavaScript
22
star
16

js-standard-http-error

Standard HTTP error class for Node.js. Proper serialization, no bloat. Extensible.
JavaScript
19
star
17

js-element-from-point

A consistent cross-browser document.elementFromPoint function. Works around idiosyncracies of old WebKits et al.
Makefile
11
star
18

node-require-guard

Prevent files and modules being required more than once. Helps with auto-reloaders or test runners that insist.
JavaScript
9
star
19

js-oolong

Object utility library for JavaScript. Simple, tasteful and plentiful. Supports inherited properties.
JavaScript
8
star
20

js-hugml

An XML parsing and serializing library based on Google's GDATA and BadgerFish conventions. Supports namespaces.
JavaScript
7
star
21

js-j6pack

JavaScript library to render JSX to JavaScript and HTML on the web or on Node.js. Works with Express.js. No React dependency, no virtual DOM.
JavaScript
7
star
22

js-co-next

JavaScript function to use generators for request handlers and middleware. Calls next only if there was an error. Useful for Express routes.
JavaScript
7
star
23

js-internet-message

Library for JavaScript to parse and stringify RFC 822 ARPA Internet Text Messages. Useful format for messages with headers and body. Similar to HTTP.
JavaScript
6
star
24

js-sqlate

A tiny tagged template string function library for JavaScript to write SQL safely. Works with Mapbox's SQLite3 library, Brian Carlson's PostgreSQL library and others.
JavaScript
6
star
25

js-lazy-object

JavaScript library to define lazy properties on objects that are initialized once and only when accessed. Also known as a lazy initialization and cached/memoized getters.
JavaScript
6
star
26

js-medium-type

JavaScript library for parsing, stringifying and manipulating media types. Useful for content negotiation. Follows RFC2045, supports quoted parameters, all allowed characters etc.
JavaScript
5
star
27

js-kindof

A proper typeof that works with primitives, built-in value objects and those from other execution contexts.
JavaScript
5
star
28

js-syslog-protocol

Syslog (RFC 3164) parser. Works with RFC 3339/ISO 8601 timestamps.
JavaScript
5
star
29

nutikaitse

CSS
5
star
30

js-fetch-parse

Fetch API mixin to buffer and parse response bodies. Supports media type patterns for content type specific custom parsers.
JavaScript
4
star
31

node-selenium-dom

SeleniumDom.js is a mixin library for Node.js's Selenium WebDriver that adds **DOM methods** to Selenium's `WebDriver` and `WebElement`.
JavaScript
4
star
32

js-square-batman

A small JavaScript scheduling algorithm library. Useful for round-robin etc.
JavaScript
4
star
33

js-fetch-jsonify

Fetch API mixin for stringifying JSON and setting Content-Type if unset. Functional and immutable.
JavaScript
3
star
34

js-egal

Strict equality test (like ===) for JavaScript that handles both built-in and custom value objects (those with a valueOf function).
JavaScript
3
star
35

node-syslogh

Log to your system's Syslog from within Node.js. Provides simple native bindings to <syslog.h> and syslog(3). Works on Node v0.10 up to v4 and beyond.
C++
3
star
36

browserify-substitution-mass-confusion

Browserify plugin for substituting modules everywhere. It expands the package.json "browser" field to work for nested dependencies.
JavaScript
3
star
37

node-fetch-off

Fetch API polyfill and facade. Its request and response wrappers can be used separately: use fetch for request yet get back Node's Http.get response (IncomingMessage) for streaming.
JavaScript
2
star
38

js-internet-message-type

Library for JavaScript to parse RFC 822 ARPA Internet Text Messages types and bodies. Works well with InternetMessage.js.
JavaScript
2
star
39

exportjs

A tool that helps you publish and export your JavaScripts to other environments by wrapping them in an IIFE. Or UMD in the future.
JavaScript
1
star
40

js-fetch-error

A JavaScript library for an error class for use with the Fetch API.
JavaScript
1
star
41

js-soul

Soul.js is a simple yet extensible mutable model library for JavaScript.
JavaScript
1
star
42

js-descend

Function to set up an inherited class that calls its ancestor constructors automatically. Can also be attached to constructors.
JavaScript
1
star
43

js-fetch-throw

A JavaScript Fetch API mixin to throw FetchError when the request fails or response has an error.
JavaScript
1
star
44

node-flash-fiction

Flash messages middleware for Express/Connect with support for redirects and immediate use. Useful for informational or errors messages. Like Rails's Flash.
JavaScript
1
star
45

js-overstrike

A utility library in the style of Underscore.js with consistent inherited property handling, the simplest implementation and curation by yours truly.
JavaScript
1
star
46

js-fetch-formify

Fetch API mixin for urlencoding an object and setting Content-Type if unset. Functional and immutable.
JavaScript
1
star