#
cp.spec
An synchronous/asynchronous test library for Lua.
This library uses a syntax similar to Ruby RSpec or Mocha.js.
#
Simple Synchronous Test
To create a test, create a new file ending with _spec.lua
. For example, simple_spec.lua
:
local spec = require "cp.spec"
local it = spec.it
return it "always passes"
:doing(function()
assert(true, "This always passes")
end)
It can be run from the Debug Console like so:
cp.spec "simple" ()
It will report something like this:
2019-10-06 18:13:28: [RESULT] it always passes: passed: 1; failed: 0; aborted: 0; time: 0.0022s
#
Simple Synchronous Failure
If a test fails, it gives a report of where it failed, and if provided, the related message:
local spec = require "cp.spec"
local it = spec.it
return it "always fails"
:doing(function()
assert(false, "This always fails")
end)
This will result in something like this:
2019-10-06 21:54:16: [FAIL] it always fails: [.../simple_spec.lua:6] This always fails
2019-10-06 21:54:16:
2019-10-06 21:54:16: [RESULT] it always fails: passed: 0; failed: 1; aborted: 0; time: 0.0370s
You can then check the line that failed and resolve the issue.
#
Simple Asynchronous Test
Performing an asynchronous test is only a little more complicated.
We'll modify our simple_spec.lua
to use of the Run.This instance available to every test:
local spec = require "cp.spec"
local it = spec.it
local timer = require "hs.timer"
return it "always passes"
:doing(function(this)
this:wait(5)
assert(true, "This happens immediately")
timer.doAfter(2, function()
assert(true, "This happens after 2 seconds.")
this:done()
end)
end)
Other than using hs.timer
to actually make this asynchronous, the key additions here are:
this:wait(5)
: Tells the test that it is asynchronous, and to wait 5 seconds before timing out.this:done()
: Called inside the asynchronous function to indicate that it's complete.
Asycnchronous (and synchronous) tests can also be terminated by a failed assert
, an error
or a call to this:fail(...)
or this:abort(...)
#
Multiple tests
Most things you're testing will require more than a single test. For this,
We use Specification, most simply via the
local spec = require "cp.spec"
local describe, it = spec.describe, spec.it
local function sum(a,b)
return a + b
end
return describe "sum" {
it "results in 3 when you add 1 and 2"
:doing(function()
assert(sum(1, 2) == 3)
end),
it "results in 0 when you add 1 and -1"
:doing(function()
assert(sum(1, -1) == 0)
end),
}
This will now run two tests, and report something like this:
2019-10-06 21:40:00: [RESULT] sum: passed: 2; failed: 0; aborted: 0; time: 0.0027s
#
Data-driven Testing
When testing a feature, there are often multiple variations you want to test, and repeating individual tests can get tedious.
This is a great place to use the where feature. Our previous test can become something like this:
return describe "sum" {
it "results in ${result} when you add ${a} and ${b}"
:doing(function(this)
assert(sum(this.a, this.b) == this.result)
end)
:where {
{ "a", "b", "result"},
{ 1, 2, 3 },
{ 1, -1, 0 },
},
}
Other variations can be added easily by adding more rows.
#
Running Multiple Specs
As shown above, you can run a single spec like so:
cp.spec "path.to.spec" ()
You can also run that spec an all other specs under the same path by adding ".*"
to the end.
cp.spec "path.to.spec.*" ()
Or run every spec in your system like so:
cp.spec "*" ()
#
Submodules
- cp.spec.DefaultHandler
- cp.spec.Definition
- cp.spec.Error
- cp.spec.Handled
- cp.spec.Handler
- cp.spec.Message
- cp.spec.Report
- cp.spec.Run
- cp.spec.Scenario
- cp.spec.Specification
- cp.spec.TestCase
- cp.spec.TestSuite
- cp.spec.Where
- cp.spec.expect
#
API Overview
Functions - API calls offered directly by the extension
describe find it setSearchPath spec test