Merge remote-tracking branch 'origin/master'
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
Some checks failed
continuous-integration/appveyor/branch AppVeyor build failed
This commit is contained in:
commit
fe2f6ccca9
6 changed files with 446 additions and 20 deletions
397
README.md
397
README.md
|
@ -24,7 +24,7 @@ $ mkdir test
|
|||
|
||||
Next in your favourite editor, create `test/test.mjs`:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.describe('Array', function() {
|
||||
|
@ -74,14 +74,14 @@ Not only does eltro allow you to use any assertion library of your own choosing,
|
|||
* `assert.notMatch(value, [message])`: Check if value does not match RegExp test.
|
||||
* `assert.isFulfilled(promise, [message])`: Assert the promise resolves.
|
||||
* `assert.isRejected(promise, [message])`: Assert the promise gets rejects.
|
||||
|
||||
# Asynchronous Code
|
||||
|
||||
# Asynchronous Code
|
||||
|
||||
Eltro supports any type of asynchronous code testing. It can either be done by adding a parameter to the function (usually done) that gets called once the tests done but eltro also supports promises.
|
||||
|
||||
Example of testing using done:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.describe('User', function() {
|
||||
|
@ -99,7 +99,7 @@ t.describe('User', function() {
|
|||
|
||||
Alternatively, just use the done() callback directly (which will handle an error argument, if it exists):
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.describe('User', function() {
|
||||
|
@ -114,7 +114,7 @@ t.describe('User', function() {
|
|||
|
||||
Or another alternative is to use promises and return a promise directly:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.test('should complete this test', function(done) {
|
||||
|
@ -126,13 +126,37 @@ t.test('should complete this test', function(done) {
|
|||
|
||||
Which works well with `async/await` like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.test('async test', async function() {
|
||||
let user = await User.find({ username: 'test' })
|
||||
assert.ok(user)
|
||||
})
|
||||
```
|
||||
|
||||
# Spying and stubbing
|
||||
|
||||
Inspired by sinon js, this library comes with pre-built simple sinon-like style spy() and stub()
|
||||
|
||||
```javascript
|
||||
import { assert, spy, stub } from 'eltro'
|
||||
|
||||
let myFunc = spy()
|
||||
let myStub = stub()
|
||||
|
||||
myFunc(1)
|
||||
myFunc(2)
|
||||
myFunc(3)
|
||||
myStub.returns('world')
|
||||
let out = myStub('hello')
|
||||
|
||||
assert.strictEqual(out, 'world')
|
||||
assert.strictEqual(myFunc.getCall(0), 1)
|
||||
assert.strictEqual(myFunc.getCall(1), 2)
|
||||
assert.strictEqual(myFunc.getCall(2), 3)
|
||||
assert.strictEqual(myFunc.callCount, 3)
|
||||
assert.strictEqual(myStub.callCount, 1)
|
||||
```
|
||||
|
||||
# Api
|
||||
|
||||
### t.test(message, func)
|
||||
|
@ -143,7 +167,7 @@ Queue up the `func` as a test with the specified messagt.
|
|||
|
||||
In case you wanna describe a bunch of tests, you can add them inside `func` and it will have the specified `message` prepended before every test:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
function someFunction() { return true }
|
||||
|
@ -167,7 +191,7 @@ will output:
|
|||
|
||||
Queue up the `func` to run before any test or groups within current active group.
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.before(function() {
|
||||
|
@ -199,7 +223,7 @@ t.describe('#anotherTest()', function() {
|
|||
|
||||
Queue up the `func` to run after any test or groups within current active group.
|
||||
|
||||
```node
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.after(function() {
|
||||
|
@ -227,13 +251,77 @@ t.describe('#anotherTest()', function() {
|
|||
})
|
||||
```
|
||||
|
||||
### t.beforeEach(func)
|
||||
|
||||
Queue up the `func` to run before each test or groups within current active group.
|
||||
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.beforeEach(function() {
|
||||
// Prepare something before each of the following tests
|
||||
})
|
||||
|
||||
t.describe('#myTest()', function() {
|
||||
t.beforeEach(function() {
|
||||
// Runs before every test in this group
|
||||
})
|
||||
|
||||
t.test('true should always be true', function() {
|
||||
assert.strictEqual(true, true)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#anotherTest()', function() {
|
||||
t.beforeEach(function() {
|
||||
// Runs before every test in this group
|
||||
})
|
||||
|
||||
t.test('false should always be false', function() {
|
||||
assert.strictEqual(false, false)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### t.afterEach(func)
|
||||
|
||||
Queue up the `func` to run after every test or groups within current active group.
|
||||
|
||||
```javascript
|
||||
import { Eltro as t, assert} from 'eltro'
|
||||
|
||||
t.afterEach(function() {
|
||||
// After we finish each individual test below, this gets run
|
||||
})
|
||||
|
||||
t.describe('#myTest()', function() {
|
||||
t.afterEach(function() {
|
||||
// Runs after each text in this group
|
||||
})
|
||||
|
||||
t.test('true should always be true', function() {
|
||||
assert.strictEqual(true, true)
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#anotherTest()', function() {
|
||||
t.afterEach(function() {
|
||||
// Runs after each text in this group
|
||||
})
|
||||
|
||||
t.test('false should always be false', function() {
|
||||
assert.strictEqual(false, false)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### t.only()
|
||||
|
||||
Eltro supports exclusivity when running tests. When specified, only tests marked with only will be run.
|
||||
|
||||
You can do exclusivity on tests by adding `.only()` in front of describe, after or before the test like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.only().describe('Only these will run', function() {
|
||||
t.test('this one', function() { assert.strictEqual(true, true) })
|
||||
t.test('and this one', function() { assert.strictEqual(true, true) })
|
||||
|
@ -242,7 +330,7 @@ t.only().describe('Only these will run', function() {
|
|||
|
||||
You can also put it on individual test like so
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.test('Only run this test', function() {
|
||||
assert.strictEqual(true, true)
|
||||
}).only()
|
||||
|
@ -250,7 +338,7 @@ t.test('Only run this test', function() {
|
|||
|
||||
or like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.only().test('Only run this test', function() {
|
||||
assert.strictEqual(true, true)
|
||||
})
|
||||
|
@ -260,7 +348,7 @@ t.only().test('Only run this test', function() {
|
|||
|
||||
You can skip tests easily by adding `.skip()` before describe, before or after the test like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.skip().describe('None of these will run', function() {
|
||||
t.test('not this', function() { assert.strictEqual(true, true) })
|
||||
t.test('or this one', function() { assert.strictEqual(true, true) })
|
||||
|
@ -269,7 +357,7 @@ t.skip().describe('None of these will run', function() {
|
|||
|
||||
You can also do it on individual tests like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.test('Skip due to something being broken', function() {
|
||||
BrokenFunction()
|
||||
}).skip()
|
||||
|
@ -277,7 +365,7 @@ t.test('Skip due to something being broken', function() {
|
|||
|
||||
or like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.skip().test('Skip this', function() { ... })
|
||||
```
|
||||
|
||||
|
@ -285,7 +373,7 @@ t.skip().test('Skip this', function() { ... })
|
|||
|
||||
Tests can take a long time. By default, eltro will cancel a test if it takes longer than 2 seconds. You can however override this by calling the timeout function after or before the test or before the describe with the specified duration in milliseconds like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.timeout(5000).describe('These will all have same timeout', function() {
|
||||
t.test('One slow function', async function() { ... })
|
||||
t.test('Another slow function', async function() { ... })
|
||||
|
@ -294,7 +382,7 @@ t.timeout(5000).describe('These will all have same timeout', function() {
|
|||
|
||||
Or apply to individual test like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.test('This is a really long test', async function() {
|
||||
await DoSomethingForReallyLongTime()
|
||||
}).timeout(5000) // 5 seconds
|
||||
|
@ -302,6 +390,277 @@ t.test('This is a really long test', async function() {
|
|||
|
||||
or like so:
|
||||
|
||||
```node
|
||||
```javascript
|
||||
t.timeout(5000).test('A long test', async function() { ... })
|
||||
```
|
||||
|
||||
# Assert
|
||||
|
||||
Eltro comes with an extended version of node's built-in assertion library.
|
||||
You can start using them by simply importing it with eltro test runner:
|
||||
|
||||
```javascript
|
||||
import { assert } from 'eltro'
|
||||
```
|
||||
|
||||
|
||||
### assert.notOk(value[, message])
|
||||
|
||||
Tests if value is a falsy value using `Boolean(value) == false`
|
||||
|
||||
```javascript
|
||||
assert.notOk(false) // ok
|
||||
assert.notOk(null) // ok
|
||||
assert.notOk(undefined) // ok
|
||||
assert.notOk([]) // throws
|
||||
```
|
||||
|
||||
### assert.match(value, test[, message])
|
||||
|
||||
Test if the string value has a regex match of test
|
||||
|
||||
```javascript
|
||||
assert.match('asdf', /a/) // ok
|
||||
assert.match('hello world', /hello/) // ok
|
||||
assert.match('something', /else/) // throws
|
||||
```
|
||||
|
||||
### assert.notMatch(value, test[, message])
|
||||
|
||||
Test if the string value does not regex match the test
|
||||
|
||||
```javascript
|
||||
assert.notMatch('asdf', /b/) // ok
|
||||
assert.notMatch('something', /else/) // ok
|
||||
assert.notMatch('hello world', /hello/) // throws
|
||||
```
|
||||
|
||||
### assert.isFulfilled(promise[, message])
|
||||
|
||||
Tests to make sure the promise gets fulfilled successfully and
|
||||
returns the final result.
|
||||
|
||||
```javascript
|
||||
await assert.isFulfilled(Promise.resolve(null)) // ok
|
||||
await assert.isFulfilled(() => { throw new Error() }) // throws
|
||||
```
|
||||
|
||||
### assert.isRejected(promise[, message])
|
||||
|
||||
Tests to make sure the promise gets rejected and returns the error
|
||||
or value that was rejected
|
||||
|
||||
```javascript
|
||||
let val = await assert.isRejected(Promise.reject('asdf')) // ok
|
||||
assert.strictEqual(val, 'asdf')
|
||||
|
||||
let err = await assert.isRejected(() => { throw new Error('hello') }) // ok
|
||||
assert.strictEqual(err.message, 'hello')
|
||||
```
|
||||
|
||||
### assert.throwsAndCatch(fn[, message])
|
||||
|
||||
Tests to make sure the function throws an exception. The important feature is this returns the original error that was thrown.
|
||||
|
||||
```javascript
|
||||
let err = assert.throwsAndCatch(() => { throw new Error('hello world') }) // ok
|
||||
assert.strictEqual(err.message, 'hello world')
|
||||
```
|
||||
|
||||
# Sinon-like spy() stub()
|
||||
|
||||
Using sinon-inspired mechanics for spying on calls as well as being able
|
||||
to stub existing functionality, eltro comes with a handy little copy-cat.
|
||||
|
||||
Functionality-wise, the difference between spy() and stub() are none.
|
||||
Both will do the exact same thing, the naming differention is simply to allow
|
||||
the resulting code to speak about its purpose.
|
||||
|
||||
To create a stub or a spy, simply import it and call it like so:
|
||||
|
||||
```javascript
|
||||
import { spy, stub } from 'eltro'
|
||||
|
||||
let spying = spy()
|
||||
let fn = stub()
|
||||
```
|
||||
|
||||
Each call to stub or spy is an array list of the passed-on arguments:
|
||||
|
||||
```javascript
|
||||
let spying = spy()
|
||||
spying('hello', 'world')
|
||||
|
||||
assert.strictEqual(spying.lastCall[0], 'hello')
|
||||
assert.strictEqual(spying.lastCall[1], 'world')
|
||||
```
|
||||
|
||||
### lastCall
|
||||
|
||||
Returns the last call that was made to the spy or stub:
|
||||
|
||||
```javascript
|
||||
let spying = spy()
|
||||
spying('a')
|
||||
spying('b')
|
||||
spying('c')
|
||||
|
||||
assert.strictEqual(spying.lastCall[0], 'c')
|
||||
```
|
||||
|
||||
### called
|
||||
|
||||
Boolean variable that gets flipped once it gets called at least once
|
||||
|
||||
```javascript
|
||||
let spying = spy()
|
||||
assert.strictEqual(spying.called, false)
|
||||
spying('a')
|
||||
assert.strictEqual(spying.called, true)
|
||||
spying('b')
|
||||
spying('c')
|
||||
assert.strictEqual(spying.called, true)
|
||||
```
|
||||
|
||||
### callCount
|
||||
|
||||
The number of times it's been called
|
||||
|
||||
```javascript
|
||||
let spying = spy()
|
||||
assert.strictEqual(spying.callCount, 0)
|
||||
spying('a')
|
||||
assert.strictEqual(spying.callCount, 1)
|
||||
spying('b')
|
||||
spying('c')
|
||||
assert.strictEqual(spying.callCount, 3)
|
||||
```
|
||||
|
||||
### returns(data)
|
||||
|
||||
Specifies what value the stub or spy should return when it gets called.
|
||||
|
||||
```javascript
|
||||
let fn = stub()
|
||||
fn.returns('a')
|
||||
|
||||
assert.strictEqual(fn(), 'a')
|
||||
assert.strictEqual(fn(), 'a')
|
||||
```
|
||||
|
||||
### throws(data)
|
||||
|
||||
Specifies what value the stub or spy should throw when it gets called.
|
||||
|
||||
```javascript
|
||||
let fn = stub()
|
||||
fn.throws(new Error('b'))
|
||||
|
||||
try {
|
||||
fn()
|
||||
} catch (err) {
|
||||
assert.strictEqual(err.message, 'b')
|
||||
}
|
||||
```
|
||||
|
||||
### resolves(data)
|
||||
|
||||
Specifies what value the stub or spy should return wrapped in a promise.
|
||||
|
||||
```javascript
|
||||
let fn = stub()
|
||||
fn.resolves('a')
|
||||
|
||||
fn().then(function(data) {
|
||||
assert.strictEqual(data, 'a')
|
||||
})
|
||||
```
|
||||
|
||||
### rejects(data)
|
||||
|
||||
Specifies what value the stub or spy should reject, wrapped in a promise.
|
||||
|
||||
```javascript
|
||||
let fn = stub()
|
||||
fn.rejects('nope')
|
||||
|
||||
fn().catch(function(data) {
|
||||
assert.strictEqual(data, 'nope')
|
||||
})
|
||||
```
|
||||
|
||||
### returnWith(fn)
|
||||
|
||||
Specify custom function to be called whenever the stub or spy gets called.
|
||||
|
||||
```javascript
|
||||
let fn = stub()
|
||||
fn.returnWith(function(a) {
|
||||
if (a === 'a') return true
|
||||
return false
|
||||
})
|
||||
|
||||
assert.strictEqual(fn(), false)
|
||||
assert.strictEqual(fn('b'), false)
|
||||
assert.strictEqual(fn('a'), true)
|
||||
assert.strictEqual(fn.callCount, 3)
|
||||
```
|
||||
|
||||
### getCall(index) getCallN(num)
|
||||
|
||||
Get a specific call. The `getCall` gets a zero-based index call while the `getCallN(num)` gets the more natural number call
|
||||
|
||||
```javascript
|
||||
let spying = spy()
|
||||
spying('a')
|
||||
spying('b')
|
||||
spying('c')
|
||||
assert.strictEqual(spying.getCall(0), 'a')
|
||||
assert.strictEqual(spying.getCall(1), 'b')
|
||||
assert.strictEqual(spying.getCallN(1), 'a')
|
||||
assert.strictEqual(spying.getCallN(2), 'b')
|
||||
```
|
||||
|
||||
### onCall(index) onCallN(num)
|
||||
|
||||
Overwrite behavior for a specific numbered call. Just like with getCall/getCallN, the onCall is zero-indexed number of the call you want to specify custom behavior while onCallN is the more natural number of the call you want to specify custom behavior.
|
||||
|
||||
Note, when called with null, it specifies the default behavior.
|
||||
|
||||
```javascript
|
||||
let fnOne = stub()
|
||||
let fnTwo = stub()
|
||||
|
||||
fnOne.onCall(1).returns('b')
|
||||
.onCall().returns('a')
|
||||
|
||||
fnTwo.onCallN(2).returns('two')
|
||||
.onCallN().returns('one')
|
||||
|
||||
assert.strictEqual(fnOne(), 'a')
|
||||
assert.strictEqual(fnOne(), 'b')
|
||||
assert.strictEqual(fnOne(), 'a')
|
||||
|
||||
assert.strictEqual(fnTwo(), 'one')
|
||||
assert.strictEqual(fnTwo(), 'two')
|
||||
assert.strictEqual(fnTwo(), 'one')
|
||||
```
|
||||
|
||||
### findCall(fn)
|
||||
|
||||
Search for the first call when `fn(call)` returns `true`. Essentially a filter to search for a specific call that matches whatever call you're searching for.
|
||||
|
||||
|
||||
```javascript
|
||||
let evnt = stub()
|
||||
|
||||
evnt('onclick', 'one')
|
||||
evnt('onerror', 'two')
|
||||
evnt('something', 'three')
|
||||
evnt('onpress', 'four')
|
||||
evnt('else', 'five')
|
||||
|
||||
let foundPressCall = evnt.findCall(function(call) { return call[0] === 'onpress' })
|
||||
assert.strictEqual(foundPressCall[0], 'onpress')
|
||||
assert.strictEqual(foundPressCall[1], 'four')
|
||||
```
|
||||
|
|
|
@ -48,6 +48,15 @@ assert.notMatch = (value, test, message) => {
|
|||
fail(m);
|
||||
}
|
||||
|
||||
assert.throwsAndCatch = (fn, message) => {
|
||||
let err = null
|
||||
assert.throws(fn, function(error) {
|
||||
err = error
|
||||
return true
|
||||
}, message)
|
||||
return err
|
||||
}
|
||||
|
||||
assert.isFulfilled = (promise, message) => {
|
||||
return Promise.resolve(true)
|
||||
.then(() => promise)
|
||||
|
|
|
@ -35,6 +35,13 @@ export function stub(returnFunc = null) {
|
|||
func.called = false
|
||||
func.callCount = 0
|
||||
|
||||
func.findCall = function(fn) {
|
||||
for (let call of calls) {
|
||||
if (fn(call)) return call
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
func.getCall = function(i) {
|
||||
return calls[i]
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eltro",
|
||||
"version": "1.3.2",
|
||||
"version": "1.3.4",
|
||||
"description": "Eltro is a tiny no-dependancy test framework for node",
|
||||
"main": "index.mjs",
|
||||
"scripts": {
|
||||
|
|
|
@ -29,6 +29,28 @@ t.describe('#notOk()', function() {
|
|||
})
|
||||
})
|
||||
|
||||
t.describe('#throwAndCatch()', function() {
|
||||
t.test('should work and return the original error', function() {
|
||||
const assertError = new Error('Speed')
|
||||
|
||||
let err = assert.throwsAndCatch(() => { throw assertError })
|
||||
assert.strictEqual(err, assertError)
|
||||
})
|
||||
|
||||
t.test('should otherwise throw if no error', function() {
|
||||
const assertMessage = 'Hello world'
|
||||
let error = null
|
||||
|
||||
try {
|
||||
assert.throwsAndCatch(() => { }, assertMessage)
|
||||
} catch (err) {
|
||||
error = err
|
||||
}
|
||||
assert.ok(error)
|
||||
assert.match(error.message, new RegExp(assertMessage))
|
||||
})
|
||||
})
|
||||
|
||||
t.describe('#isFulfilled()', function() {
|
||||
t.test('should exist', function() {
|
||||
assertExtended.ok(assertExtended.isFulfilled)
|
||||
|
|
|
@ -96,6 +96,35 @@ import { spy, stub } from '../index.mjs'
|
|||
assert.strictEqual(spyer.thirdCall[0], assertThirdArgs[0])
|
||||
assert.strictEqual(spyer.thirdCall[1], assertThirdArgs[1])
|
||||
})
|
||||
|
||||
|
||||
t.test('should support searching for a call', function() {
|
||||
const assertFirstArgs = 'asdf'
|
||||
const assertSecondArgs = { a: 1 }
|
||||
const assertThirdArgs = [{ b: 1 }, { c: 2 }]
|
||||
let spyer = tester()
|
||||
assert.notOk(spyer.called)
|
||||
|
||||
spyer(assertFirstArgs)
|
||||
spyer(assertSecondArgs)
|
||||
spyer(assertThirdArgs[0], assertThirdArgs[1])
|
||||
|
||||
let call = spyer.findCall((args) => args[0] === assertSecondArgs)
|
||||
assert.strictEqual(spyer.secondCall, call)
|
||||
assert.strictEqual(call[0], assertSecondArgs)
|
||||
|
||||
call = spyer.findCall((args) => args[0].b === assertThirdArgs[0].b)
|
||||
assert.strictEqual(spyer.thirdCall, call)
|
||||
assert.strictEqual(call[0], assertThirdArgs[0])
|
||||
assert.strictEqual(call[1], assertThirdArgs[1])
|
||||
|
||||
call = spyer.findCall((args) => typeof args[0] === 'string')
|
||||
assert.strictEqual(spyer.firstCall, call)
|
||||
assert.strictEqual(call[0], assertFirstArgs)
|
||||
|
||||
call = spyer.findCall(() => false)
|
||||
assert.strictEqual(call, null)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in a new issue