shard-spectator/README.md

287 lines
9.1 KiB
Markdown
Raw Normal View History

2018-08-18 21:33:20 +00:00
Spectator
=========
2018-09-27 20:57:58 +00:00
Spectator is a fully-featured spec-based test framework for Crystal.
It provides more functionality from [RSpec](http://rspec.info/)
than the built-in Crystal [Spec](https://crystal-lang.org/api/latest/Spec.html) utility.
2018-09-27 20:57:58 +00:00
Additionally, Spectator provides extra features to make testing easier and more fluent.
2018-08-18 21:33:20 +00:00
2018-11-02 20:32:36 +00:00
**Goal:**
Spectator is designed to:
- Reduce complexity of test code.
- Remove boilerplate from tests.
- Lower the difficulty of writing non-trivial tests.
- Provide an elegant syntax that is easy to read and understand.
- Provide common utilities that the end-user would otherwise need to write.
2018-08-18 21:33:20 +00:00
Installation
------------
Add this to your application's `shard.yml`:
```yaml
2018-09-27 21:02:50 +00:00
development_dependencies:
2018-08-18 21:33:20 +00:00
spectator:
gitlab: arctic-fox/spectator
```
Usage
-----
If it doesn't exist already, create a `spec/spec_helper.cr` file.
In it, place the following:
2018-08-18 21:33:20 +00:00
```crystal
require "spectator"
require "../src/*"
```
This will include Spectator and the source code for your shard.
Now you can start writing your specs.
The syntax is the same as what you would expect from modern RSpec.
The "expect" syntax is recommended and the default, however the "should" syntax is also available.
Your specs must be wrapped in a `Spectator.describe` block.
2018-09-27 21:02:50 +00:00
All other blocks inside the top-level block may use `describe` and `context` without the `Spectator.` prefix.
Here's a minimal spec to demonstrate:
```crystal
require "./spec_helper"
Spectator.describe String do
subject { "foo" }
describe "#==" do
context "with the same value" do
let(value) { subject.dup }
it "is true" do
is_expected.to eq(value)
end
end
context "with a different value" do
let(value) { "bar" }
it "is false" do
is_expected.to_not eq(value)
end
end
end
end
2018-08-18 21:33:20 +00:00
```
If you find yourself trying to shoehorn in functionality
2018-09-27 21:02:50 +00:00
or unsure how to write a test, please create an [issue](https://gitlab.com/arctic-fox/spectator/issues/new) for it.
2019-03-28 02:08:06 +00:00
The goal is to make it as easy as possible to write specs and keep your code clean.
2018-11-02 20:32:36 +00:00
We may come up with a solution or even introduce a feature to support your needs.
NOTE: Due to the way this shard uses macros,
2018-09-27 21:02:50 +00:00
you may find that some code you would expect to work, or works in other spec libraries, creates syntax errors.
2019-03-28 02:08:06 +00:00
If you run into this, please create an issue so that we may try to resolve it.
Features
--------
2019-03-28 02:08:06 +00:00
Spectator has all of the basic functionality for BDD.
### Contexts
The DSL supports arbitrarily nested contexts.
Contexts can have values defined for multiple tests (`let` and `subject`).
Additionally, hooks can be used to ensure any initialization or cleanup is done (`before`, `after`, and `around`).
Pre- and post-conditions can be used to ensure code contracts are kept.
Spectator has different types of contexts to reduce boilerplate.
One is the `sample` context.
This context type repeats all tests (and contexts within) for a set of values.
For instance, some feature should behave the same for different input.
However, some inputs might cause problems, but should behave the same.
An example is various strings (empty strings, quoted strings, strings with non-ASCII, etc),
and numbers (positive, negative, zero, NaN, infinity).
Another context type is `given`.
This context drastically reduces the amount of code needed in some scenarios.
It can be used where one (or more inputs) changes the output of multiple methods.
The `given` context gives a concise syntax for this use case.
### Assertions
Spectator supports two formats for assertions (expectations).
The preferred format is the "expect syntax".
This takes the form:
```crystal
expect(THIS).to eq(THAT)
```
The other format, "should syntax" is used by Crystal's default Spec.
```
THIS.should eq(THAT)
```
The first format doesn't monkey-patch the `Object` type.
And as a bonus, it captures the expression or variable passed to `expect()`.
For instance, compare these two tests:
```crystal
foo = "Hello world"
foo.size.should eq(12) # Wrong on purpose!
```
Produces this error output:
```text
Failure: 11 does not equal 12
expected: 11
actual: 12
```
Which is reasonable, but where did 11 come from?
Alternatively, with the "expect syntax":
```crystal
foo = "Hello world"
expect(foo.size).to eq(12) # Wrong on purpose!
```
Produces this error output:
```text
Failure: foo.size does not equal 12
expected: 12
actual: 11
```
This makes it clearer what was being tested and failed.
### Matchers
Spectator has a variety of matchers for assertions.
These are named in such a way to help tests read as plain English.
Matchers can be used on any value or block.
There are typical matchers for testing equality: `eq` and `ne`.
And matchers for comparison: `<`, `<=`, `>`, `>=`, `be_within`.
There are matchers for checking contents of collections:
`contain`, `have`, `start_with`, `end_with`, `be_empty`, `have_key`, and more.
See the full documentation for a full list of matchers.
### Running
Spectator supports multiple options for running tests.
"Fail fast" aborts on the first test failure.
"Fail blank" fails if there are no tests.
Tests can be filtered by their location and name.
Additionally, tests can be randomized.
Spectator can be configured with command-line arguments, a config block in a `spec_helper.cr` file, and `.spectator` config file.
### Output
Spectator matches Crystal's default Spec output with some minor changes.
JUnit and TAP are also supported output formats.
There is also a highly detailed JSON output.
2018-08-18 21:33:20 +00:00
Development
-----------
2019-03-28 02:08:06 +00:00
This shard is still under development and is not recommended for production use (same as Crystal).
However, feel free to play around with it and use it for non-critical projects.
### Feature Progress
2019-03-28 02:08:06 +00:00
In no particular order, features that have been implemented and are planned.
Items not marked as completed may have partial implementations.
2018-08-30 21:15:01 +00:00
- [ ] DSL
- [X] `describe` and `context` blocks
2018-09-04 22:59:59 +00:00
- [X] Contextual values with `let`, `let!`, `subject`, `described_class`
- [X] Test multiple and generated values - `sample`, `random_sample`
- [X] Concise syntax - `given`
2018-09-15 15:59:55 +00:00
- [X] Before and after hooks - `before_each`, `before_all`, `after_each`, `after_all`, `around_each`
- [X] Pre- and post-conditions - `pre_condition`, `post_condition`
2018-09-22 18:35:44 +00:00
- [ ] Other hooks - `on_success`, `on_failure`, `on_error`
- [X] One-liner syntax
2018-09-27 22:11:45 +00:00
- [X] Should syntax - `should`, `should_not`
- [X] Helper methods and modules
2018-09-13 01:11:36 +00:00
- [ ] Aliasing - custom example group types with preset attributes
- [X] Pending tests - `pending`
2018-09-13 01:11:36 +00:00
- [ ] Shared examples - `behaves_like`, `include_examples`
2018-08-30 21:15:01 +00:00
- [ ] Matchers
2019-02-01 22:40:43 +00:00
- [X] Equality matchers - `eq`, `ne`, `be ==`, `be !=`
2019-02-02 04:37:48 +00:00
- [X] Comparison matchers - `be <`, `be <=`, `be >`, `be >=`, `be_within[.of]`, `be_close`
2019-03-29 05:20:32 +00:00
- [X] Type matchers - `be_a`, `respond_to`
- [ ] Collection matchers
- [X] `contain`
- [X] `have`
- [X] `contain_exactly`
- [ ] `contain_exactly.in_any_order`
- [X] `match_array`
- [ ] `match_array.in_any_order`
- [X] `start_with`
- [X] `end_with`
- [X] `be_empty`
- [X] `have_key`
- [X] `have_value`
- [ ] `all`
- [ ] `all_satisfy`
2019-02-01 22:40:43 +00:00
- [X] Truthy matchers - `be`, `be_true`, `be_truthy`, `be_false`, `be_falsey`, `be_nil`
2019-03-22 04:50:02 +00:00
- [X] Error matchers - `raise_error`
2019-02-01 22:40:43 +00:00
- [ ] Yield matchers - `yield_control[.times]`, `yield_with_args[.times]`, `yield_with_no_args[.times]`, `yield_successive_args`
- [ ] Output matchers - `output[.to_stdout|.to_stderr]`
- [ ] Misc. matchers
- [ ] `exist`
- [X] `match`
- [ ] `satisfy`
- [ ] `change[.by|.from[.to]|.to|.by_at_least|.by_at_most]`
- [X] `have_attributes`
- [ ] Compound - `and`, `or`
2018-08-30 21:15:01 +00:00
- [ ] Runner
2019-03-22 06:14:36 +00:00
- [X] Fail fast
2018-09-13 01:11:36 +00:00
- [ ] Test filtering - by name, context, and tags
- [X] Fail on no tests
2019-03-24 01:56:00 +00:00
- [X] Randomize test order
2019-03-22 20:18:03 +00:00
- [X] Dry run - for validation and checking formatted output
- [X] Config block in `spec_helper.cr`
- [X] Config file - `.spectator`
2019-03-23 23:01:03 +00:00
- [X] Reporter and formatting
2019-03-03 19:00:56 +00:00
- [X] RSpec/Crystal Spec default
2019-03-23 03:52:48 +00:00
- [X] JSON
2019-03-23 23:01:03 +00:00
- [X] JUnit
2019-03-23 06:25:46 +00:00
- [X] TAP
2019-03-28 02:08:06 +00:00
### How it Works (in a nutshell)
This shard makes extensive use of the Crystal macro system to build classes and modules.
Each `describe` and `context` block creates a new module nested in its parent.
2019-03-28 02:08:06 +00:00
The `it` block creates an example class.
An instance of the example class is created to run the test.
Each example class includes a context module, which contains all test values and hooks.
2018-08-18 21:33:20 +00:00
Contributing
------------
1. Fork it (<https://gitlab.com/arctic-fox/spectator/fork/new>)
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Merge Request
2019-03-28 02:08:06 +00:00
Please make sure to run `crystal tool format` before submitting.
The CI build checks for properly formatted code.
Tests must be written for any new functionality.
Macros that create types are not as easy to test,
so they are exempt for the current time.
However, please test all code locally with an example spec file.
2018-08-18 21:33:20 +00:00
Contributors
------------
- [arctic-fox](https://gitlab.com/arctic-fox) Michael Miller - creator, maintainer