Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement test environments and before/after closures #30

Merged
merged 48 commits into from
Sep 1, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2c466e9
Add support for test context environments
regexident Dec 26, 2016
f69294c
Move environment into Runner (changing strategy from BFS to DFS in PS…
regexident Aug 15, 2017
b25a374
Add support for `before` and `after` (and variants) closures
regexident Aug 17, 2017
f8be924
Clean up project structure
regexident Aug 17, 2017
5a10889
Make the whole thing parallel in execution.
regexident Aug 19, 2017
f0b1aa6
Make runner work with multiple suites, remove unnecessary lifetimes, …
regexident Aug 19, 2017
020d2b2
Rustfmt the files + move runner::Configuration in its own module
twickham Aug 20, 2017
95812d3
move Context modules in a directory
twickham Aug 20, 2017
e67b7f4
cargo fmt
twickham Aug 20, 2017
6feb165
Merge pull request #32 from mackwic/extract_context_to_module_dir
mackwic Aug 20, 2017
4e7b0c3
Extract private method and rename inner variables
twickham Aug 20, 2017
1c08f99
remove useless `_with` methods in Runner
twickham Aug 20, 2017
a54bbd6
Remove need for `rayon::initialize`
regexident Aug 21, 2017
091cfa5
Remove unused/commented code from example
regexident Aug 21, 2017
899f4a5
Demote suite/context/example names from `String` to `&’static str`
regexident Aug 23, 2017
c96b477
Lots of cleanup, a parallel formatter, and lots more
regexident Aug 24, 2017
a32a110
Add convenience macro and example
regexident Aug 24, 2017
40fe66c
Improved README
regexident Aug 25, 2017
8cbc9b4
Fix `.or_exit()`, making it less error prone while we’re at it
regexident Aug 25, 2017
0b37f9a
Refactor Runner simplifying `fn visit()` for `Context<T>`
regexident Aug 25, 2017
b55e424
Improve module exports & documentation
regexident Aug 25, 2017
eeee79c
Improve `EventHandler` (now called `RunnerObserver`)
regexident Aug 25, 2017
651d84a
Improve documentation for `ExampleReport`
regexident Aug 25, 2017
f6b7ea3
Clean up suite/context/example header structs & add tests
regexident Aug 26, 2017
c365034
Add default impl for `RunnerObserver` trait
regexident Aug 26, 2017
5b36cc1
Move root environment into Suite
regexident Aug 26, 2017
cf21311
cargo fmt
twickham Aug 26, 2017
9fc46f7
Add minimal tests for suite
regexident Aug 26, 2017
be79277
Fix indentation of Context tests
regexident Aug 26, 2017
d8428fa
Improve imports
regexident Aug 26, 2017
98773f8
Clean up `RunnerObserver`
regexident Aug 26, 2017
e6cbc61
Change `RunnerObserver` default impls to stable syntax
regexident Aug 26, 2017
2b2eca3
impl default for configuration runner
twickham Aug 27, 2017
4333c68
first tests of the runner
twickham Aug 27, 2017
7b6cde0
str is simpler than String
twickham Aug 27, 2017
19d9428
add Runner::wrap_each tests
twickham Aug 27, 2017
f2f515c
add Runner tests for wrap_all and Drop trait
twickham Aug 27, 2017
9ca53f4
Add first test for impl_visitor_block_for_runner
twickham Aug 27, 2017
09cd99d
Fix macro, allowing passing of expression, not just identifiers
regexident Aug 27, 2017
eb546ba
Fix missing new-line in formatter
regexident Aug 27, 2017
6a7401b
Simplify ‘simple’ example
regexident Aug 27, 2017
137be3e
Replace `rspec_run!` macro with function
regexident Aug 27, 2017
2519af8
Some more cleanup
regexident Aug 27, 2017
c30782f
Fix test runner::impl_visitor_block_for_runner::it_can_be_called
twickham Aug 28, 2017
8e3ee5e
Add tests for impl_visitor_example_for_runner
twickham Aug 28, 2017
9b9bbbf
Rename ‘Formatter’ to ‘Logger’
regexident Aug 31, 2017
97cab2b
Improve logger, merging serial & parallel logger
regexident Aug 31, 2017
97b2f62
Fix documentation for `rspec::run(…)` function
regexident Aug 31, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 44 additions & 10 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
# Compiled files
*.o
*.so
*.rlib
*.dll

# Executables
*.exe
# Created by https://www.gitignore.io/api/rust

### Rust ###
# Generated by Cargo
# will have compiled files and executables
/target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
Cargo.lock
target
Cargo.lock
*.bk

# These are backup files generated by rustfmt
**/*.rs.bk

# These are temporary files generated by racer
*.racertmp

# End of https://www.gitignore.io/api/rust

# Created by https://www.gitignore.io/api/osx

### OSX ###
*.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# End of https://www.gitignore.io/api/osx
.idea/*
*.iml
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ rust:
- nightly
- beta
- stable
- 1.9.0
- 1.19.0
matrix:
allow_failures:
- rust: nightly
Expand Down
35 changes: 28 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
[package]
name = "rspec"
authors = [
"Thomas Wickham <mackwic@gmail.com>",
"Vincent Esche <regexident@gmail.com>",
]
description = "Write Rspec-like tests with stable rust"
version = "1.0.0-beta.3"
authors = ["Thomas Wickham <mackwic@gmail.com>"]
license = "MPL-2.0"
homepage = "https://mackwic.github.io/rspec"
repository = "https://github.com/mackwic/rspec"
keywords = [
"rspec",
"test",
"harness",
"tdd",
"bdd",
]
license = "MPL-2.0"
name = "rspec"
readme = "README.md"
keywords = ["rspec", "test", "harness", "bdd"]
repository = "https://github.com/mackwic/rspec"
version = "1.0.0-beta.4"

[build-dependencies.clippy]
optional = true
version = "0.0.153"

[dependencies]
clippy = {version = "0.0", optional = true}
colored = "1.4.0"
derive-new = "0.5.0"
derive_builder = "0.5.0"
rayon = "0.8.2"

[dependencies.expectest]
optional = true
version = "0.9.1"

[features]
default = []
expectest_compat = ["expectest"]
150 changes: 95 additions & 55 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ The last stable documentation is available for consultation at
[mackwic.github.io/rspec](https://mackwic.github.io/rspec).

**All rspec releases are garanteed to compile against the latest stable rust and
are tested on all rust versions from the 1.9**.
are tested on all rust versions from the 1.19**.

## How to use

Expand All @@ -32,90 +32,130 @@ extern crate rspec;

You can see complete examples in the [`examples/`](https://github.com/mackwic/rspec/tree/master/examples) directory.

You can now use rspec in your unit tests, this example will use the `cargo test`
runner:

```rust
fn add(x: u32, y: u32) -> u64 {
x + y
}
extern crate rspec;

#[test]
fn test_add() {
rdescribe("add", |ctx| {
ctx.describe("0 <= x + y <= u32::MAX", |ctx| {
ctx.it("2 + 4 = 6", || {
assert_eq!(6, add(2, 4))
pub fn main() {
// Use a local struct to provide the test contexts with an environment.
// The environment will contain the subject that is to be tested
// along with any additional data you might need during the test run:
#[derive(Clone, Default, Debug)]
struct Environment {
// ...
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the Environment is an advanced feature that should not be used by default for every test. Maybe split the example in 2, one really simple, and another that showcase the Environment use ?


// `rspec::run(…)` is a convenience wrapper that takes care of setting up
// a runner, logger, configuration and running the test suite for you.
// If you want more direct control, you can manually set up those things, too.
rspec::run(&rspec::describe("rspec, a BDD testing framework", Environment::default(), |ctx| {
// `describe`, or any of its equivalents, opens the root context
// of your test suite. Within you can then either define test examples:
ctx.it("can define top-level tests", |_| true);

// or make use of sub-contexts to add some structure to your test suite:
ctx.specify("contexts give your tests structure and reduce redundancy", |ctx| {

ctx.before(|_| {
// Executed once, before any of the contexts/examples is entered.
});

ctx.it("4 + 4 = 8", || {
assert_eq!(8, add(4, 4))
ctx.after(|_| {
// Executed once, after all of the contexts/examples have been exited.
});

ctx.specify("rspec can handle results", |ctx| {
ctx.it("passes if the return is_ok()", |_| Ok(()) as Result<(),()>);
ctx.it("failes if the return is_err()", |_| Err(()) as Result<(),()>);
});

ctx.describe("rspec can handle bools", |ctx| {
ctx.it("should pass if true", |_| true);
ctx.it("should fail if false", |_| false);
ctx.it("is convenient for comparisons", |_| (42 % 37 + 2) > 3);
});

ctx.describe("rspec can handle units", |ctx| {
ctx.it("should pass if the return is ()", |_| {});
});

ctx.describe("rspec can handle panics", |ctx| {
ctx.it("is convenient for asserts", |_| assert_eq!(1, 1));
});
});

ctx.it("is associative", || {
assert_eq!(add(2, 1), add(1, 2));
assert_eq!(add(4, 1), add(1, 4));
assert_eq!(add(4, 5), add(5, 4));
assert_eq!(add(12, 1), add(1, 12));
});
});
})); // exits the process with a failure code if one of the tests failed.
}
```

You can also use rspec in integration tests, this example uses the rspec runner:
### Suites, Contexts & Examples

```rust
extern crate rspec;
use rspec::context::describe;
use std::io;
rspec provides three variants for each of the structural elements:

pub fn main() {
let stdout = &mut io::stdout();
let mut formatter = rspec::formatter::Simple::new(stdout);
let mut runner = describe("rspec is a classic BDD testing", |ctx| {
| | Variant A | Variant B | Variant C |
|-----------|------------|------------|------------|
| Suites: | `suite` | `describe` | `given` |
| Contexts: | `context` | `specify` | `when` |
| Examples: | `example` | `it` | `then` |

**Note:** While the intended use is to stick to a single variant per test suite
it is possible to freely mix structural elements across variants.

ctx.it("can define tests", || true);
#### Variant A: `suite`, `context` & `example`

ctx.describe("rspec use results for tests results", |ctx| {
```rust
runner.run(&rspec::suite("opens a suite", /* environment */, |ctx| {
ctx.context("opens a context", |ctx| {
ctx.example("opens an example", |env| /* test condition */ );
});
}));
```

ctx.it("passed if the return is_ok()", || Ok(()) as Result<(),()>);
#### Variant B: `describe`, `specify` & `it`

ctx.it("failed if the return is_err()", || Err(()) as Result<(),()>);
});
```rust
runner.run(&rspec::describe("opens a suite", /* environment */, |ctx| {
ctx.specify("opens a context", |ctx| {
ctx.it("opens an example", |env| /* test condition */ );
});
}));
```

ctx.describe("rspec can use bools", |ctx| {
#### Variant C: `given`, `when` & `then`

ctx.it("should pass if true", || true);
```rust
runner.run(&rspec::given("opens a suite", /* environment */, |ctx| {
ctx.when("opens a context", |ctx| {
ctx.then("opens an example", |env| /* test condition */ );
});
}));
```

ctx.it("should fail if false", || false);
### Before & After

ctx.it("is convenient for comparisons", || {
(42 % 37 + 2) > 3
})
});
| | All | Each |
|---------|-----------------------|---------------|
| Before: | `before`/`before_all` | `before_each` |
| After: | `after` /`after_all` | `after_each` |

ctx.describe("rspec can use units", |ctx| {
#### All

ctx.it("should pass if the return is ()", || {});
The "All" variants of before and after blocks are executed once upon
entering (or exiting, respectively) the given context.

ctx.it("is convenient for asserts", || assert_eq!(1, 1));
});
});
runner.add_event_handler(&mut formatter);
runner.run().unwrap();
}
#### Each

```
`before_each` and `after_each` blocks are executed once before each of the
given context's sub-contexts or examples.

*Note:*
- `describe` has 4 aliases: `specify`, `context`, `given`, and `when`
- `it` has 2 aliases: `example`, and `then`
### More Examples

Again, you can see complete examples in the [`examples/`](https://github.com/mackwic/rspec/tree/master/examples) directory.

## Documentation

The last stable documentation is available for consultation at
[mackwic.github.io/rspec](https://mackwic.github.io/rspec).
[https://docs.rs/rspec](https://docs.rs/rspec).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the new Readme is quite great ! 👌


## Contributions

Expand Down
56 changes: 56 additions & 0 deletions examples/advanced.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
extern crate rspec;

use std::collections::BTreeSet;

pub fn main() {
#[derive(Clone, Debug)]
struct Environment {
set: BTreeSet<usize>,
len_before: usize,
}

let environment = Environment {
set: BTreeSet::new(),
len_before: 0,
};

rspec::run(&rspec::given("a BTreeSet", environment, |ctx| {
ctx.when("not having added any items", |ctx| {
ctx.then("it is empty", |env| assert!(env.set.is_empty()));
});

ctx.when("adding an new item", |ctx| {
ctx.before_all(|env| {
env.len_before = env.set.len();
env.set.insert(42);
});

ctx.then("it is not empty any more", |env| {
assert!(!env.set.is_empty());
});

ctx.then("its len increases by 1", move |env| {
assert_eq!(env.set.len(), env.len_before + 1);
});

ctx.when("adding it again", |ctx| {
ctx.before_all(|env| {
env.len_before = env.set.len();
env.set.insert(42);
});

ctx.then("its len remains the same", move |env| {
assert_eq!(env.set.len(), env.len_before);
});
});
});

ctx.when("returning to outer context", |ctx| {
ctx.then("it is still empty", |env| assert!(env.set.is_empty()));
});

ctx.then("panic!(…) fails", move |_env| {
panic!("Some reason for failure.")
});
}));
}
22 changes: 0 additions & 22 deletions examples/given_when_then.rs

This file was deleted.

Loading