UNPKG

7.65 kBMarkdownView Raw
1# Hamjest
2
3A library of composable matchers for defining meaningful and readable assertions in JavaScript. Based on [Hamcrest](http://hamcrest.org).
4
5# Why?
6
7Because readable assertions and readable _assertion errors_ really matter. Failing with `Expected false to equal true` just doesn't cut it. Matcher-based assertions help you extend and maintain large test suites by explaining _why exactly_ an assertion failed.
8
9See the [matcher documentation](https://github.com/rluba/hamjest/wiki/Matcher-documentation) for a list of available matchers.
10
11Hamjest…
12
13* … tries to deliver meaningful and readable error descriptions, even for arbitrary JavaScript objects,
14* … uses deep equivalence (without coercion) as default matcher - instead of '==' or '===',
15* … has builtin support for asynchronous tests and assertions using promises,
16* … lets [Lo-Dash](http://lodash.com) do some of the heavy lifting (because you can't do it any better by yourself),
17* … is designed as a first-class [NPM module](https://npmjs.org/hamjest),
18* … has a build for browsers (i.e. you can use it in [Karma](http://karma-runner.github.io) tests and whatnot),
19* … has an extensive suite of [Mocha](http://mochajs.org/) tests
20
21# Installation
22Hamjest is available via [NPM](https://npmjs.org/package/hamjest):
23
24```Shell
25npm install hamjest --save-dev
26```
27
28# Usage
29
30All asserts and matchers are available as children of the `hamjest` module, so just require it, give it an unobtrusive name and start matching:
31
32```JavaScript
33var __ = require('hamjest');
34
35__.assertThat('jim the rat', __.containsString('rat'));
36__.assertThat(5, __.is(__.greaterThan(2)));
37__.assertThat([5, 12, 9], __.hasItem(__.greaterThanOrEqualTo(11)));
38```
39
40The best thing about Hamjest are its error messages, just like the [Java original](http://hamcrest.org):
41
42```JavaScript
43var sut = {name: 1337, age: 25};
44__.assertThat(sut, __.hasProperties({name: __.string(), age: __.greaterThan(18)}));
45
46AssertionError:
47Expected: an object with {name: a string, age: a number greater than <18>}
48 but: name was a Number (<1337>)
49```
50
51You can also add a descriptive message to every assert, if needed:
52
53```JavaScript
54__.assertThat('Invalid age', age, __.lessThan(18));
55
56AssertionError: Invalid age
57Expected: a number less than <18>
58 but: was <18>
59```
60
61See the [matcher documentation](https://github.com/rluba/hamjest/wiki/Matcher-documentation) for a list of available matchers.
62
63Have a look at the [test suite](./test/) to see lots of usage examples [for each matcher](./test/matchers/) as well as the [assertThat](./test/assertThatSpec.js) and [promiseThat](./test/promiseThatSpec.js) functions.
64
65See the [documentation about promises](https://github.com/rluba/hamjest/wiki/Hamjest-and-Promises) for details about using Hamjest with promises or asserting asynchronously.
66
67## JSON descriptions
68
69Notice how the mismatching value is described as part of the AssertionError, so you don't have to fire up the debugger every time an assertion fails. This also works for arbitrary JavaScript objects:
70
71```JavaScript
72__.assertThat({name: 'custom object'}, __.equalTo({name: 'another object'}));
73
74AssertionError:
75Expected: {"name":"another object"}
76 but: was {"name":"custom object"}
77```
78
79## FeatureMatcher
80Not impressed? I'll give it another try, using the builtin `FeatureMatcher`:
81
82```JavaScript
83// Define a custom matcher using FeatureMatcher:
84function animalWithName(matcherOrValue) {
85 return new __.FeatureMatcher(matcherOrValue, 'animal with name', 'name');
86}
87
88var animal = {name: 'Bob', age: 12};
89__.assertThat(animal, __.is(animalWithName('Tom')));
90
91AssertionError:
92Expected: is animal with name "Tom"
93 but: name of {"name":"Bob","age":12} was "Bob"
94```
95
96By default, FeatureMatcher tries to find a property with the given feature name (the third parameter), but you can pass in an optional feature function:
97
98```JavaScript
99function animalWithNameLength(matcherOrValue) {
100 return new __.FeatureMatcher(matcherOrValue, 'animal with name length', 'name length', function (item) {
101 return item.name.length;
102 });
103}
104
105var animal = {name: 'bob', age: 12};
106__.assertThat(animal, __.is(animalWithNameLength(__.greaterThan(5))));
107
108AssertionError:
109Expected: is animal with name length a number greater than <5>
110 but: name length of {"name":"bob","age":12} was <3>
111```
112
113## Suggestions
114Do you have an idea how to make a matcher's error description even more readable? Does Hamjest lack a crucial matcher? (I'm sure it does...)
115
116Just send me a message (I'm [@LubaRaph on Twitter](https://twitter.com/lubaraph)), open a ticket or - even better - send me a pull request.
117
118# Browser support
119Hamjest also runs in the browser - thanks to [browserify](http://browserify.org/).
120
121Simply include `dist/hamjest(.min).js` in your browser tests. It comes with "batteries included" and none of the dependencies are leaked into global scope.
122
123The browser build exports a single global: `hamjest`. You can rename it as usual for better readability:
124
125```JavaScript
126var __ = hamjest;
127
128__.assertThat('2007-05-01', __.startsWith('2007'));
129```
130
131# Development
132
133You need [Gulp](http://gulpjs.com) to lint and test the project and build the browser version.
134
135```Shell
136npm install -g gulp
137```
138
139Run `gulp build` to lint and test the project and update the browser files. Use `gulp dev` during development to run linting and tests whenever any JS file changes.
140
141# Breaking changes between versions
142
143## v2.x to v3.0
144
145* Hamjest 3 requires at least Node.js 4.x, since it some ES6 syntax. Stick to Hamjest 2 if you still use older versions of Node.js.
146* The browser version of Hamjest now always contains its own dependencies (lodash, bluebird). Previously it also offered a version that looked for these dependencies in global scope. but that caused more trouble than it solved.
147* The formatting for many mismatch descriptions has changed to make them easier to read (added, indentation, newlines, …)
148
149 In particular, Hamjest 3 now uses a special description format for DOM-like objects (eg. DOM nodes, [cheerio](https://www.npmjs.com/package/cheerio) objects, …) to make mismatch descriptions involving those kinds of objects easier to understand.
150
151 I consider this a breaking change since Hamjest’s readable console messages are its primary feature.
152* Replaced Q with Bluebird for all promise-related code (`promiseThat(…)`, `willBe(…)`, etc.). If you previously used any of Q's non-standard sugar methods on the promises returned by Hamjest, you'll need to switch to the equivalent [Bluebird methods](http://bluebirdjs.com/docs/api-reference.html).
153* Internal: Switched from Grunt to Gulp. This might affect you if you created custom builds of Hamjest.
154
155## v1.x to v2.0
156### throws
157`throws` now behaves like other matchers that accept a sub-matcher. If an argument is provided, it can now be a matcher or an arbitrary value, i.e. it is wrapped with `equalTo`, if it is not a matcher.
158
159Previously, the argument had to be the expected exception type and was always wrapped in `instanceOf`. To get the old behavior, change
160
161```JavaScript
162__.assertThat(fn, __.throws(AssertionError))
163```
164
165to
166
167```JavaScript
168__.assertThat(fn, __.throws(__.instanceOf(AssertionError)))
169```
170
171## v0.x to v1.0
172### promiseThat
173The semantics of `promiseThat` has changed in `1.0.0` Previously the sub-matcher was called with the fulfilled value instead of the promise. This turned out to be of limited use because you couldn't test for rejection.
174
175# License
176
177Licensed under the MIT License [(enclosed)](./LICENSE).
178
179This library is inspired by and based on the work of the original [Hamcrest team](http://hamcrest.org).