README.md 10.4 KB
Newer Older
1
2
<div align="center">
    <a href="https://gitlab.com/distilled/distilled/tree/develop"><img alt="Distilled logo breakdown" src="https://gitlab.com/distilled/distilled/raw/develop/icons/distilled-details.jpg" width="200px" ></a>
Daniel Shumway's avatar
Daniel Shumway committed
3
    <a href="https://distilledjs.com"><img alt="Distilled logo" src="https://gitlab.com/distilled/distilled/raw/develop/icons/distilled.jpg" width="200px" ></a>
4
    <br><br>
5
    <a href="https://nodei.co/npm/@latinfor/distilled/"><img alt="npm stats" src="https://nodei.co/npm/@latinfor/distilled.png?compact=true"></a>
6
7
    <br>
    <a href="https://gitlab.com/distilled/distilled/commits/stable"><img alt="build status" src="https://gitlab.com/distilled/distilled/badges/stable/build.svg" /></a>
Daniel Shumway's avatar
Daniel Shumway committed
8
    <a href="https://badge.fury.io/js/%40latinfor%2Fdistilled"><img src="https://badge.fury.io/js/%40latinfor%2Fdistilled.svg" alt="npm version" height="18"></a>
9
    <a href="https://opensource.org/licenses/mit-license.php"><img alt="MIT License" src="https://badges.frapsoft.com/os/mit/mit.png?v=103"></a>
Daniel Shumway's avatar
Daniel Shumway committed
10
11
    <!-- Switch this to Patreon Link -->
    <!-- <a href="https://gratipay.com/Distilled"><img alt="Donate" src="https://img.shields.io/gratipay/Distilled.svg?style=social&label=Donate&maxAge=2592000"></a> -->
12
13
</div>

14
15
# Distilled

Daniel Shumway's avatar
Daniel Shumway committed
16
The *un*opinionated testing framework. Learn more at [distilledjs.com](https://distilledjs.com).
Daniel Shumway's avatar
Daniel Shumway committed
17
18
19

## What is Distilled?

20
21
Distilled is an aggressively elegant testing framework. It is designed to
religiously follow two rules:
Daniel Shumway's avatar
Daniel Shumway committed
22

Daniel Shumway's avatar
Daniel Shumway committed
23
24
- Get out of the user's way when they *don't* want to make decisions.
- Get out of the user's way when they *do* want to make decisions.
25

Daniel Shumway's avatar
Daniel Shumway committed
26
## When you *don't* want to make decisions
27

28
29
30
31
32
The biggest barrier standing in the way of good testing for your projects is
boilerplate code and other forms of friction. Testing frameworks front-load you
with decisions before you even start coding -- what will your assertion style
be? How will your tests be structured? Do you need integration tests? How up to
date are you on the documentation?
33

Daniel Shumway's avatar
Daniel Shumway committed
34
Distilled makes it easy to defer these questions until later when you're more
35
36
37
equipped to answer them. This means that when you start a new project, you can
begin testing immediately, without wasting time worrying about future
architecture decisions or re-reading documentation.
Daniel Shumway's avatar
Daniel Shumway committed
38

39
40
Distilled's entire testing API is only one method, because the smaller an API
is, the easier it is to remember.
41
42
43
44
45
46
47
48
49
50

```js
var Suite = new Distilled();
suite.test('test label', function () { 
	if (condition) {
		throw 'exception';
	}
});
```

51
Distilled is designed to be installed and configured in most projects in less
52
53
than one minute. To prove this, let's add Distilled to a NodeJS project right
now.
54

55
In your terminal, install Distilled:
56

57
```bash
58
npm install --save-dev @latinfor/distilled
59
60
61
```

Make a new file for your tests:
62

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
```js
var Distilled = require('distilled-distilled');
var assert = require('assert');
var library = require('../my-library');

var suite = new Distilled();

var module = suite.test('My Library Test');
module.test('Methods: ', function (test) {
	test.test('`add` adds two numbers together', function () {
		assert.deepEqual(library.add(2, 3), 5);
	});

	test.test('`subtract` subtracts two numbers', function () {
		assert.deepEqual(library.subtract(3, 2), 1, 'normal usage');
		assert.deepEqual(library.subtract(2, 3), -1, 'less than zero');
	});
});
```

Open up your ``package.json`` and add a test runner:
84

85
86
87
88
89
90
91
92
```json
{
   "scripts": {
		"test": "node tests/libraryTest.js"
	}
}
```

Daniel Shumway's avatar
Daniel Shumway committed
93
94
And we're done! In your command line, run ``npm test`` and check out the
results. You now know everything you need to know to get started using Distilled.
95

96
97
Of course, if you're just starting out a new project even this might be too much
friction. I often set up my testing suite in the same file as my prototype code.
Daniel Shumway's avatar
Daniel Shumway committed
98
99
100
This allows me to write tests alongside my prototype right from the start. I
wait to pull out my tests into a separate file until after I've spent some time
experimenting in code.
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

Lets go over the fastest way to set up Distilled with a completely brand new
project:

```bash
npm init -y # Worry about names and licenses later
npm install --save-dev @latinfor/distilled
```

Create a new file, ``myPrototype.js``:

```js
function fibonacci (index) {
  if (index <= 1) return 1;
  return fibonacci(index - 1) + fibonacci(index - 2);
}

//------TESTS--------

var Distilled = require('@latinfor/distilled');
var suite = new Distilled();
suite.test('fibonacci', function () {
  this.test('0', fibonacci(0) === 1);
  this.test('1', fibonacci(1) === 1);
  this.test('2', fibonacci(2) === 2);
  this.test('5', fibonacci(5) === 8);
});
```

And done! Run your tests with ``node ./myPrototype.js``.


Daniel Shumway's avatar
Daniel Shumway committed
133
## When you *do* want to make decisions
134

Daniel Shumway's avatar
Daniel Shumway committed
135
136
There are multiple opinionated testing frameworks that promise some variant of
the above. They say that removing choices will mean you have fewer things to
Daniel Shumway's avatar
Daniel Shumway committed
137
138
139
140
141
think about.

However, taking away user choices only serves to force you to make decisions
*even earlier*! Before even installing an opinionated framework, you now need
to figure out whether it will support all of your future needs.
142

143
144
145
146
Distilled is not an opinionated framework -- it is almost absurdly flexible. And
as your codebase matures and you do start to make decisions about testing
architecture and project needs, Distilled will support you no matter what those
decisions are.
147

148
149
150
The reason why Distilled can stay unopinionated while exposing a tiny API (3
methods, total), is because each part of its API is designed to be easily
manipulated and extended on-the-fly by experienced coders.
151

152
153
154
In other words, Distilled's API is *composable*, which means it doesn't need to
have an opinion on how most testing features will be implemented. Instead, users
can easily implement those features themselves in exactly the way they want.
155

Daniel Shumway's avatar
Daniel Shumway committed
156
Distilled is proud to ship *without* the following features:
157

Daniel Shumway's avatar
Daniel Shumway committed
158
159
160
161
162
163
- An assertion library 
- ``setup`` or ``teardown`` hooks
- ``ignore`` options for old tests
- test retry support
- test perfomance logging
- global variable detection
Daniel Shumway's avatar
Daniel Shumway committed
164
165
- test file detection/globbing
- even a CLI runner!
166
167
168
169

Don't be afraid -- the majority of these features won't be required for most of
your projects (remember from earlier that complexity should only be added when
needed). And any of these features that you do end up needing are simple and
Daniel Shumway's avatar
Daniel Shumway committed
170
painless to add yourself.
171

Daniel Shumway's avatar
Daniel Shumway committed
172
173
174
Distilled is based on two innovations that make adding new features easy.
Wrapping your head around these concepts will change the way you think about
writing tests.
175

Daniel Shumway's avatar
Daniel Shumway committed
176
The first is recursively resolving tests that allow you to build your own assertions:
177

Daniel Shumway's avatar
Daniel Shumway committed
178
```js
Daniel Shumway's avatar
Daniel Shumway committed
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
suite.test('Accept promise', Promise.resolve()); //passes
suite.test('Accept boolean', false); //fails
suite.test('Accept nothing'); //passes
suite.test('Accept function', function () {}); //passes
suite.test('Accept function that returns boolean', function () {
	return false;
}); //fails
suite.test('Accept function that returns promise', function () {
	return Promise.reject();
}); //fails
suite.test('Accept function that returns function that returns promise', function () {
	return function () {
		return Promise.reject();
	};
}); //Fails
suite.test('Accept promise that resolves to function that returns promise that resolves to boolean', Promise.resolve(function () {
    return Promise.resolve(false);
})); //Fails
197
198
```

Daniel Shumway's avatar
Daniel Shumway committed
199
The second is infinitely chainable/nestable tests that allow you to flexibly structure suites:
200
201

```js
Daniel Shumway's avatar
Daniel Shumway committed
202
203
204
205
var setup = suite.test('Parent', Promise.resolve()); //Passes
var child = setup.test('child', function () {
	return Promise.reject('error');
}); //Fails once the parent has finished
206

Daniel Shumway's avatar
Daniel Shumway committed
207
208
209
var subchild = setup.test('sub-child', function () {
	return Promise.resolve();
}); //Is never run
210
211
```

212
213
214
215
216
217
218
219
If the ``test`` method isn't enough for you, Distilled also exposes an
``assert`` and ``then`` method, which open up Distilled even farther for
power-users.

Distilled is purposefully and unapologetically unopinionated, because I believe
the second-biggest barrier standing in the way of good testing for your projects
are opinionated, inflexible frameworks that can't handle your specific problems
and goals.
220

221
222
223
224
225
Distilled is designed to be adapted and extended to fit your project, not the
other way around. This means you can spend less time trying to fit square pegs
into round holes, less time worrying about whether you'll be able to handle new
requirements in the future, and more time thinking creatively about how you can
use testing to make your code safer and feature iteration faster.
226

Daniel Shumway's avatar
Daniel Shumway committed
227
## Contributing
228

Daniel Shumway's avatar
Daniel Shumway committed
229
230
231
Distilled follows Test Driven Development principles. This includes bug reports.
Every bug report *must* be accompanied by a code sample that demonstrates an
expected behavior. Bugs will not triaged until that code sample is added.
Daniel Shumway's avatar
Daniel Shumway committed
232
233

For example, if you found that Distilled crashed whenever the ``Date``
234
235
constructor was called, your bug report might include a code block that looked
like this:
236
237

```js
Daniel Shumway's avatar
Daniel Shumway committed
238
var suite = new Distilled();
239

Daniel Shumway's avatar
Daniel Shumway committed
240
241
242
243
244
suite.test('', function () {
    var date = new Date();
}).then(function () {
   console.log('Distilled does not crash when the `Date` constructor is called');
   console.log('`Date` constructor does not force test to fail: ', this.status === Distilled.STATUS.PASSED);
245
246
247
});
```

Daniel Shumway's avatar
Daniel Shumway committed
248
If you submit a bug without a code block demonstrating what you would expect to
249
250
251
252
253
happen, then I'll mark the bug as incorrectly filed and wait for you to fix your
mistake. If you do submit a code example and it needs rewriting or
restructuring, don't worry -- I'll work with you to do that. Once there's a code
sample that we both agree demonstrates what the correct behavior should be, then
I'll triage the bug and decide its priority.
Daniel Shumway's avatar
Daniel Shumway committed
254

255
For the most part, filing bugs is more valuable than submitting pull requests.
Daniel Shumway's avatar
Daniel Shumway committed
256
In fact, you should expect that most pull requests will be rejected, even if the
257
pull request contains unit tests and is generally well written.
Daniel Shumway's avatar
Daniel Shumway committed
258

259
260
261
262
This is because Distilled is aggressive about maintaining a simple, consistent,
and flexible API. Very few decisions are made without a lot of thought. New
features aren't added until it's obvious to me that they are necessary. And even
then, they aren't added until I'm certain their design is rock-solid.
Daniel Shumway's avatar
Daniel Shumway committed
263
264
265
266
267

There are lots of features and extensions for Distilled that are good ideas, and
most of them should be implemented as separate packages. You can of course feel
free to publish your own testing extensions and frameworks that are built on top
of Distilled.
268
269
270

If you do decide to submit a pull-request, and I decide to accept it, it will
likely need to be accompanied by a copyright assignment.