Must.js
=======
[![NPM version][npm-badge]](https://www.npmjs.com/package/must)
[![Build status][travis-badge]](https://travis-ci.org/moll/js-must)

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][rfc2119] 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][mocha].

[npm-badge]: https://img.shields.io/npm/v/must.svg
[travis-badge]: https://travis-ci.org/moll/js-must.png?branch=master
[rfc2119]: https://www.ietf.org/rfc/rfc2119.txt
[mocha]: https://mochajs.org

### Tour
- Assert with a **beautiful and fluent chain** that saves you from wrapping
  objects manually and reads nicely, too:
  ```javascript
  require("must/register")
  obj.must.be.true()
  ```

- Supports the **expect flavor** of wrapping as well:
  ```javascript
  var demand = require("must")
  demand(obj).be.string()
  ```

- **Many expressive matchers** out of the box, including:
  ```javascript
  [].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](#asserting-on-property-access) is
  **dangerous** in other assertion libraries.

- Has an **intelligent and type-safe** recursive [`eql`][Must.prototype.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.
  ```javascript
  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.

  ```javascript
  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][rfc2119] 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][should.js] and [Chai.js][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](#asserting-on-property-access).

### 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](#plugins) by others available, too.


Installing
----------
**Note**: Must.js will follow the [semantic versioning](http://semver.org/)
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][browserify] to have it run there till then.

[browserify]: https://github.com/substack/node-browserify


Using
-----
To use the **fluent chain**, just require Must.js's "register" file and it'll
make itself available everywhere:
```javascript
require("must/register")
```

Then just access the `must` property on any object and call matchers on it.
```javascript
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:
```javascript
var expect = require("must")
var demand = require("must")
```

And call it with the object you wish to assert:
```javascript
expect(answer).to.equal(42)
demand(null).be.null()
```

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

### Negative asserting or matching the opposite
To assert the opposite, just add `not` between the chain:
```javascript
true.must.not.be.false()
[].must.not.be.empty()
```

Use it multiple times to create lots of fun puzzles! :-)
```javascript
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:

```javascript
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`][Must.prototype.property] matcher:

```javascript
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][mocha], that file is `test/mocha.opts`:
```
--require must/register
```

### Full example
Inside a test runner or framework things would look something like this:
```javascript
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][api].

[api]: https://github.com/moll/js-must/blob/master/doc/API.md

### [Must](https://github.com/moll/js-must/blob/master/doc/API.md#Must)
- [.prototype.a](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.a)(class)
- [.prototype.above](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.above)(expected)
- [.prototype.after](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.after)(expected)
- [.prototype.an](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.an)(class)
- [.prototype.array](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.array)()
- [.prototype.at](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.at)
- [.prototype.be](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.be)(expected)
- [.prototype.before](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.before)(expected)
- [.prototype.below](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.below)(expected)
- [.prototype.between](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.between)(begin, end)
- [.prototype.boolean](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.boolean)()
- [.prototype.contain](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.contain)(expected)
- [.prototype.date](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.date)()
- [.prototype.empty](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.empty)()
- [.prototype.endWith](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.endWith)(expected)
- [.prototype.enumerable](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.enumerable)(property)
- [.prototype.enumerableProperty](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.enumerableProperty)(property)
- [.prototype.eql](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.eql)(expected)
- [.prototype.equal](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.equal)(expected)
- [.prototype.error](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.error)([constructor], [expected])
- [.prototype.eventually](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.eventually)
- [.prototype.exist](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.exist)()
- [.prototype.false](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.false)()
- [.prototype.falsy](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.falsy)()
- [.prototype.frozen](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.frozen)()
- [.prototype.function](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.function)()
- [.prototype.gt](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.gt)(expected)
- [.prototype.gte](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.gte)(expected)
- [.prototype.have](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.have)
- [.prototype.include](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.include)(expected)
- [.prototype.instanceOf](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.instanceOf)(class)
- [.prototype.instanceof](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.instanceof)(class)
- [.prototype.is](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.is)(expected)
- [.prototype.keys](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.keys)(keys)
- [.prototype.least](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.least)(expected)
- [.prototype.length](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.length)(expected)
- [.prototype.lt](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.lt)(expected)
- [.prototype.lte](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.lte)(expected)
- [.prototype.match](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.match)(regexp)
- [.prototype.most](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.most)(expected)
- [.prototype.must](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.must)
- [.prototype.nan](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.nan)()
- [.prototype.nonenumerable](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.nonenumerable)(property)
- [.prototype.nonenumerableProperty](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.nonenumerableProperty)(property)
- [.prototype.not](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.not)
- [.prototype.null](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.null)()
- [.prototype.number](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.number)()
- [.prototype.object](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.object)()
- [.prototype.own](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.own)(property, [value])
- [.prototype.ownKeys](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.ownKeys)(keys)
- [.prototype.ownProperties](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.ownProperties)(properties)
- [.prototype.ownProperty](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.ownProperty)(property, [value])
- [.prototype.permutationOf](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.permutationOf)(expected)
- [.prototype.properties](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.properties)(properties)
- [.prototype.property](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.property)(property, [value])
- [.prototype.regexp](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.regexp)()
- [.prototype.reject](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.reject)
- [.prototype.resolve](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.resolve)
- [.prototype.startWith](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.startWith)(expected)
- [.prototype.string](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.string)()
- [.prototype.symbol](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.symbol)()
- [.prototype.the](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.the)
- [.prototype.then](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.then)
- [.prototype.throw](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.throw)([constructor], [expected])
- [.prototype.to](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.to)
- [.prototype.true](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.true)()
- [.prototype.truthy](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.truthy)()
- [.prototype.undefined](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.undefined)()
- [.prototype.with](https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.with)


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][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][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`][Must.prototype.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
```

<a name="asserting-on-property-access"></a>
### Beware of libraries that assert on property access
Among other things, one reason why [Should.js][should.js] and
[Chai.js][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:
```javascript
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()`.

[should.js]: https://github.com/visionmedia/should.js
[chai.js]: http://chaijs.com


Plugins
-------
- [must-sinon](https://www.npmjs.com/package/must-sinon) ([Repository](https://github.com/JohnnyEstilles/must-sinon)) — Sinon assertions.
- [must-targaryen](https://www.npmjs.com/package/must-targaryen) ([Repository](https://github.com/jtwebman/must-targaryen)) — Firebase Targaryen assertions.
- [must-jsx](https://www.npmjs.com/package/must-jsx) ([Repository](https://github.com/nwinch/must-jsx)] — React.js JSX assertions.

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](http://themoll.com)** typed this and the code.  
[Monday Calendar](https://mondayapp.com) supported the engineering work.

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

[email]: mailto:andri@dot.ee
[issues]: https://github.com/moll/js-must/issues

[Must.prototype.eql]: https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.eql
[Must.prototype.property]: https://github.com/moll/js-must/blob/master/doc/API.md#Must.prototype.property
