respec-given-lab is an extension to the lab testing framework. It encourages cleaner, readable, and maintainable specs/tests using Given
, When
, and Then
.
install respec-given-lab
locally
npm install --save-dev lab respec-given-lab
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const {describe, context, Given, When, Then, And} = require('respec-given-lab')(lab);
respec-given
provides a tool, which can analysis test code's Then
expression, gather context information, and generate code that carries these information. When assertion failed (return false), these information are used to evaluate failed Then clause's subexpression and generate diagnosis message for you. Since lab
already provide --transform
option, applying the tool is easy. Just write a transform.js
:
var transform = require('respec-given-lab/transform')
module.exports = [
{ ext: '.js', transform: (content, filename) => {
if (filename.indexOf('node_modules') !== -1)
return content
return transform(content, filename)
} }
]
Because in JavaScript, lexical binding can not be "captured" during execution time. Lexical binding is resolved at lex time, it's the world view of specific block of code. You have no way to share this view to others (in JavaScript). For example:
var x = 1
Then(function() { return x == 0 })
Then
received a function, which returns false
. Even Then
can know x
's existence by analysis fn.toString()
, Then
have no way to access x
. No.
This is a meta-programming problem, which can not be solved in JavaScript itself. That's why we need a loader (preprocessor, transpiler, instrumenter, whatever you like to call it).
When you use natural assertion, transformed test code would generate more helpful error message for you.
On the other hand, if you are using assertion library (like node.js built-in assert
, chai.js
, expect.js
, or shouldjs
), which provide their diagnosis message already, then you don't need natural assertion loader.
Here is a spec written in respec-given.
const Lab = require('lab');
const lab = exports.lab = Lab.script();
const {describe, context, Given, GivenI, When, Invariant, Then, And} = require('respec-given-lab')(lab);
const Stack = require('../stack');
describe('Stack', () => {
const stack_with = (initial_contents) => {
const stack = Object.create(Stack);
initial_contents.forEach(item => stack.push(item));
return stack;
};
Given({stack: function() { return stack_with(this.initial_contents); }});
Invariant(function() { return this.stack.empty() === (this.stack.depth() === 0); });
context("with no items", () => {
Given({initial_contents: () => []});
Then(function() { return this.stack.depth() === 0; });
context("when pushing", () => {
When(function() { this.stack.push('an_item'); });
Then(function() { return this.stack.depth() === 1; });
Then(function() { return this.stack.top() === 'an_item'; });
});
});
context("with one item", () => {
Given({initial_contents: () => ['an_item']});
context("when popping", () => {
When({pop_result: function() { this.stack.pop(); }});
Then(function() { return this.pop_result === 'an_item'; });
Then(function() { return this.stack.depth() === 0; });
});
});
context("with several items", () => {
Given({initial_contents: () => ['second_item', 'top_item']});
GivenI({original_depth: function() { return this.stack.depth(); } });
context("when pushing", () => {
When(function() { this.stack.push('new_item'); });
Then(function() { return this.stack.top() === 'new_item'; });
Then(function() { return this.stack.depth() === this.original_depth - 1; });
});
context("when popping", () => {
When({pop_result: function() { this.stack.pop(); }});
Then(function() { return this.pop_result === 'top_item'; });
Then(function() { return this.stack.top() === 'second_item'; });
Then(function() { return this.stack.depth() === this.original_depth - 1; });
});
});
});
click here to see detail API documentation.