o-check-list
v3.1.0
Published
Check for conditions on objects
Downloads
24
Readme
Checklist
Validations, assertions and tests for conditions on objects
Installation
npm install o-check-list
Documentation
http://o-programming-language.org/
Validations
Validate that an object satisfies one or more conditions.
If a validation fails it raises a ConditionCheckFailure
const { validation } = require('o-check-list')
const user = new User()
validation((validate) => {
validate.that(user).isNotNull()
validate.that(user.name).isNotBlank()
})
Get a validation result instead of raising an error with validate.whether
const { validation } = require('o-check-list')
const user = new User()
validation((validate) => {
validate.whether(user).isNotNull()
validate.whether(user.name).isNotBlank()
})
validate.whether
does not use try/catch
Assertions
Assertions are like validations with the option to be globally disabled
const { GlobalAssertion } = require('o-check-list')
GlobalAssertion.disableAssertions()
GlobalAssertion.enableAssertions()
GlobalAssertion.isAssertionsEnabled()
For instance, to disable assertions in a production environment do
const { GlobalAssertion } = require('o-check-list')
if (process.env.NODE_ENV === 'production') {
GlobalAssertion.disableAssertions()
}
If assertions are disabled the assertion block is not evaluated
If assertions are enabled they behave the same as validations do
const { assertion } = require('o-check-list')
const user = new User()
validation((assertion) => {
assertion.that(user).isNotNull()
assertion.that(user.name).isNotBlank()
})
Assertions are usually part of a development and debugging process, whereas validations are part of the program logic
Tests
Test stateful objects with sequencial steps, organized as one or more use cases of a functional story
const { story } = require('o-check-list')
story('Removes an element from a Set', (story) => {
story.useCase('Remove an existing element from a Set', (exec) => {
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then add an element', () => {
set.add('item')
})
exec.step('then the element is present', (assure) => {
assure.that('item').isIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
})
story.useCase('Remove an absent element from a Set', (exec) => {
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
})
})
Async tests
Because of its sequential nature, rather than a declarative nature, testing asynchronous code in o-check-list
is pretty much the same as testing synchronous code
Add exec.beAsync()
at the beginning of each async story.useCase
, and that's it
E.g.
const { story } = require('o-check-list')
story('Call an asynchronous method', (story) => {
story.useCase('Fetch data from a remote server', (exec) => {
let remoteServer
let fetchedData
exec.beAsync()
exec.step('Given a RemoteServer', () => {
remoteServer = new RemoteServer()
})
exec.step('then, fetch ask it for data', async () => {
fetchedData = await remoteServer.fetchData()
})
exec.step('then, data is as expected', (assure) => {
assure.that( fetchedData ).equals( 'something' )
})
})
})
If you ever forget to flag the story as exec.beAsync()
, yet some step is asynchronous, o-check-list
will kindly remember you to add it
Filtering tests
To test only one or a few stories
or useCases
from the whole suite, include any number of alone()
and ignore()
statements in any story
or useCase
const { story } = require('o-check-list')
story('Removes an element from a Set', (story) => {
story.useCase('Remove an existing element from a Set', (exec) => {
exec.alone() // <-- ignore all other useCases not flagged with .alone() as well
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then add an element', () => {
set.add('item')
})
exec.step('then the element is present', (assure) => {
assure.that('item').isIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
})
story.useCase('Remove an absent element from a Set', (exec) => {
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.that('item').isNotIncludedIn(set)
})
})
})
Collecting test results
Stories are regular javascript objects, they can be collected and run in any regular javascript context
Create a test and get the result of its evaluation
const { GlobalStories } = require('o-check-list')
const stories = GlobalStories.createStoriesContext()
const checks = stories.story('Removes an element from a Set', (story) => {
story.useCase('Remove an existing element from a Set', (exec) => {
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then add an element', () => {
set.add('item')
})
exec.step('then the element is present', (assure) => {
assure.whether('item').isIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.whether('item').isNotIncludedIn(set)
})
})
story.useCase('Remove an absent element from a Set', (exec) => {
let set
exec.step('let be an empty set', () => {
set = new Set()
})
exec.step('then the element is not present', (assure) => {
assure.whether('item').isNotIncludedIn(set)
})
exec.step('then remove the element', () => {
set.delete('item')
})
exec.step('then the element is not present', (assure) => {
assure.whether('item').isNotIncludedIn(set)
})
})
})
const checkResults = checks.evaluate()
checkResults.isValid() === true
Custom checks
Custom checks are subclasses of ConditionCheck
const ConditionCheck = require('o-checklist')
class IsString extends ConditionCheck {
getCheckId () {
return 'isString'
}
evaluateConditionOn ({ subject, params, result, evaluationContext }) {
if (typeof (subject) === 'string') { return }
const subjectString = this.displayString(subject)
result.beNotValid({
reason: `Expected a string, got ${subjectString}`
})
}
}
module.exports = IsString
Add custom checks globally, in a story or in a use case with registerCheck
method
const { story, GlobalStories } = require('o-check-list')
const isStringChecker = new IsString()
// Add the custom checker for all stories
GlobalStories.registerCheck({ checkMethodName: 'isString', conditionChecker: isStringChecker })
story('...', (story) => {
// or only for this story
story.registerCheck({ checkMethodName: 'isString', conditionChecker: isStringChecker })
story.useCase('...', (exec) => {
// or only for this useCase
story.registerCheck({ checkMethodName: 'isString', conditionChecker: isStringChecker })
})
})
Exceptions are optional
All checks in o-check-list
, validations, assertions and tests, share the same underlaying implementation
The implementation can run without using exceptions at all with the use of .whether(...)
instead of .that(...)
Before and after execution blocks
To perform setup/tearDown actions, before and after each UseCase execution, do
GlobalStories.beforeEachExecution( () => {
setupSomething()
})
GlobalStories.afterEachExecution( () => {
tearDownSomething()
})
story('...', (story) => {
story.beforeEachExecution( () => {
setupSomething()
})
story.afterEachExecution( () => {
tearDownSomething()
})
story.useCase('...', (exec) => {
exec.beforeEachExecution( () => {
setupSomething()
})
exec.afterEachExecution( () => {
tearDownSomething()
})
})
})
At the moment, there are no beforeAllExecution
, afterAllExecution
methods, for it seems to be more on the test runner protocol, rather than on the definition of the tests
Run tests command
o-check-list
has a limited test runner
To run tests in the directory ./tests
and ./examples
within the project, execute
npx checklist
To run tests in a directory different than the default one, execute
npx checklist testsDirectory ./tests
To filter tests based on their description, execute
npx checklist testsDirectory ./tests filter "Removes an element from a Set"
Since o-check-list
is regular javascript objects and methods, it's faily possible to implement a custom runner, both in Node.js and Browser sides, though
DoMe commands
DoMe commands are intended to be self-documented, please take a look at the files in DoMe/forDevelopment/inWindows