@latinfor/distilled
v1.2.0
Published
An aggressively elegant testing framework, inspired by ideals of simplicity, flexibility, and consistency
Downloads
25
Maintainers
Readme
Distilled
The unopinionated testing framework. Learn more at distilledjs.com.
What is Distilled?
Distilled is an aggressively elegant testing framework. It is designed to religiously follow two rules:
- 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.
When you don't want to make decisions
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?
Distilled makes it easy to defer these questions until later when you're more 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.
Distilled's entire testing API is only one method, because the smaller an API is, the easier it is to remember.
var Suite = new Distilled();
suite.test('test label', function () {
if (condition) {
throw 'exception';
}
});
Distilled is designed to be installed and configured in most projects in less than one minute. To prove this, let's add Distilled to a NodeJS project right now.
In your terminal, install Distilled:
npm install --save-dev @latinfor/distilled
Make a new file for your tests:
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:
{
"scripts": {
"test": "node tests/libraryTest.js"
}
}
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.
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. 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.
Lets go over the fastest way to set up Distilled with a completely brand new project:
npm init -y # Worry about names and licenses later
npm install --save-dev @latinfor/distilled
Create a new file, myPrototype.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
.
When you do want to make decisions
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 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.
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.
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.
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.
Distilled is proud to ship without the following features:
- An assertion library
setup
orteardown
hooksignore
options for old tests- test retry support
- test perfomance logging
- global variable detection
- test file detection/globbing
- even a CLI runner!
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 painless to add yourself.
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.
The first is recursively resolving tests that allow you to build your own assertions:
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
The second is infinitely chainable/nestable tests that allow you to flexibly structure suites:
var setup = suite.test('Parent', Promise.resolve()); //Passes
var child = setup.test('child', function () {
return Promise.reject('error');
}); //Fails once the parent has finished
var subchild = setup.test('sub-child', function () {
return Promise.resolve();
}); //Is never run
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.
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.
Contributing
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.
For example, if you found that Distilled crashed whenever the Date
constructor was called, your bug report might include a code block that looked
like this:
var suite = new Distilled();
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);
});
If you submit a bug without a code block demonstrating what you would expect to 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.
For the most part, filing bugs is more valuable than submitting pull requests. In fact, you should expect that most pull requests will be rejected, even if the pull request contains unit tests and is generally well written.
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.
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.
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.