[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/buttercup a277b0e 022/340: Setup and teardown: before-each
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/buttercup a277b0e 022/340: Setup and teardown: before-each, after-each, before-all, after-all |
Date: |
Thu, 16 Dec 2021 14:58:57 -0500 (EST) |
branch: elpa/buttercup
commit a277b0e7d1860cb0880c701267e32c56c534763d
Author: Jorgen Schaefer <contact@jorgenschaefer.de>
Commit: Jorgen Schaefer <contact@jorgenschaefer.de>
Setup and teardown: before-each, after-each, before-all, after-all
---
README.md | 136 ++++++++++++++++++++++++++++++++++++++++++++++++------
buttercup-test.el | 81 +++++++++++++++++++++++++++++++-
buttercup.el | 81 ++++++++++++++++++++++++++++++--
3 files changed, 280 insertions(+), 18 deletions(-)
diff --git a/README.md b/README.md
index 6d3d202..db8149b 100644
--- a/README.md
+++ b/README.md
@@ -155,11 +155,122 @@ that are not included below.
(expect bar :to-throw 'void-variable '(a))))))
```
-## Spies
+## Grouping Related Specs with `describe`
-Buttercup provides a way of _spying_ on a function, something usually
-called mocking, but Jasmine calls it _spies_, and so do we. Did I
-mention Buttercup is heavily inspired by Jasmine?
+The `describe` macro is for grouping related specs. The string
+parameter is for naming the collection of specs, and will be
+concatenated with specs to make a spec’s full name. This aids in
+finding specs in a large suite. If you name them well, your specs read
+as full sentences in traditional
+[BDD](http://en.wikipedia.org/wiki/Behavior-driven_development) style.
+
+```Lisp
+(describe "A spec"
+ (it "is just a function, so it can contain any code"
+ (let ((foo 0))
+ (setq foo (1+ foo))
+
+ (expect foo :to-equal 1)))
+
+ (it "can have more than one expectation"
+ (let ((foo 0))
+ (setq foo (1+ foo))
+
+ (expect foo :to-equal 1)
+ (expect t :to-equal t))))
+```
+
+### Setup and Teardown
+
+To help a test suite DRY up any duplicated setup and teardown code,
+Buttercup provides the `before-each`, `after-each`, `before-all` and
+`after-all` special forms.
+
+As the name implies, code blocks defined with `before-each` are called
+once before each spec in the `describe` is run, and the `after-each`
+code blocks are called once after each spec.
+
+Here is the same set of specs written a little differently. The
+variable under test is defined at the top-level scope — the `describe`
+block — and initialization code is moved into a `before-each` block.
+The `after-each` block resets the variable before continuing.
+
+```Lisp
+(describe "A spec using `before-each' and `after-each'"
+ (let ((foo 0))
+ (before-each
+ (setq foo (1+ foo)))
+
+ (after-each
+ (setq foo 0))
+
+ (it "is just a function, so it can contain any code"
+ (expect foo :to-equal 1))
+
+ (it "can have more than one expectation"
+ (expect foo :to-equal 1)
+ (expect t :to-equal t))))
+```
+
+The `before-all` form is called only once before all the specs in
+`describe` are run, and the `after-all` form is called after all specs
+finish. These functions can be used to speed up test suites with
+expensive setup and teardown.
+
+However, be careful using `before-all` and `after-all`! Since they are
+not reset between specs, it is easy to accidentally leak state between
+your specs so that they erroneously pass or fail.
+
+```Lisp
+(describe "A spec using `before-all' and `after-all'"
+ (let (foo)
+ (before-all
+ (setq foo 1))
+
+ (after-all
+ (setq foo 0))
+
+ (it "sets the iniital value of foo before specs run"
+ (expect foo :to-equal 1)
+ (setq foo (1+ foo)))
+
+ (it "does not reset foo between specs"
+ (expect foo :to-equal 2))))
+```
+
+### Nesting `describe` Blocks
+
+Calls to `describe` can be nested, with specs defined at any level.
+This allows a suite to be composed as a tree of functions. Before a
+spec is executed, Buttercup walks down the tree executing each
+`before-each` function in order. After the spec is executed, Buttercup
+walks through the `after-each` functions similarly.
+
+```Lisp
+(describe "A spec"
+ (let (foo)
+ (before-each
+ (setq foo 0)
+ (setq foo (1+ foo)))
+
+ (after-each
+ (setq foo 0))
+
+ (it "is just a function, so it can contain any code"
+ (expect foo :to-equal 1))
+
+ (it "can have more than one expectation"
+ (expect foo :to-equal 1)
+ (expect t :to-equal t))
+
+ (describe "nested inside a second describe"
+ (let (bar)
+ (before-each
+ (setq bar 1))
+
+ (it "can reference both scopes as needed"
+ (expect foo :to-equal bar))))))
+```
## Test Runners
@@ -167,13 +278,12 @@ Evaluating `describe` forms just stores the suites. You
need to use a
test runner to actually evaluate them. Buttercup comes with two test
runners by default:
-- `buttercup-run-suite-at-point` — Evaluate the topmost `describe`
- form at point and run the suite it creates directly. Useful for
- interactive development. But be careful, this uses your current
- environment, which might not be clean (due to said interactive
- development).
-- `buttercup-discover` — Find files in directories specified on the
- command line, load them, and then run all suites defined therein.
- Useful for being run in batch mode.
-- `buttercup-markdown-runner` — Run code in markdown files. Used to
+- `buttercup-run-at-point` — Evaluate the topmost `describe` form at
+ point and run the suite it creates directly. Useful for interactive
+ development. But be careful, this uses your current environment,
+ which might not be clean (due to said interactive development).
+- `buttercup-run-discover` — Find files in directories specified on
+ the command line, load them, and then run all suites defined
+ therein. Useful for being run in batch mode.
+- `buttercup-run-markdown` — Run code in markdown files. Used to
run this file’s code.
diff --git a/buttercup-test.el b/buttercup-test.el
index 933c770..b3e4cb9 100644
--- a/buttercup-test.el
+++ b/buttercup-test.el
@@ -19,6 +19,9 @@
(require 'buttercup)
+;;;;;;;;;;
+;;; expect
+
(describe "The buttercup-failed signal"
(it "can be raised"
(expect (lambda ()
@@ -125,7 +128,13 @@
(buttercup--apply-matcher :not-defined '(1 2)))
:to-throw)))
-;; Built-in matchers are tested in README.md
+;;;;;;;;;;;;;;;;;;;;;
+;;; Built-in matchers
+
+;; Are tested in README.md
+
+;;;;;;;;;;;;;;;;;;;;
+;;; Suites: describe
(describe "The `buttercup-suite-add-child' function"
(it "should add an element at the end of the list"
@@ -187,6 +196,9 @@
:to-equal
desc2)))))
+;;;;;;;;;;;;;
+;;; Specs: it
+
(describe "The `it' macro"
(it "should expand to a call to the `buttercup-it' function"
(expect (macroexpand '(it "description" body))
@@ -211,3 +223,70 @@
(expect (funcall (buttercup-spec-function spec))
:to-equal
23)))))
+
+;;;;;;;;;;;;;;;;;;;;;;
+;;; Setup and Teardown
+
+(describe "The `before-each' macro"
+ (it "expands to a function call"
+ (expect (macroexpand '(before-each (+ 1 1)))
+ :to-equal
+ '(buttercup-before-each (lambda () (+ 1 1))))))
+
+(describe "The `buttercup-before-each' function"
+ (it "adds its argument to the before-each list of the current suite"
+ (let* ((suite (make-buttercup-suite))
+ (buttercup--current-suite suite))
+ (buttercup-before-each 23)
+
+ (expect (buttercup-suite-before-each suite)
+ :to-equal
+ (list 23)))))
+
+(describe "The `after-each' macro"
+ (it "expands to a function call"
+ (expect (macroexpand '(after-each (+ 1 1)))
+ :to-equal
+ '(buttercup-after-each (lambda () (+ 1 1))))))
+
+(describe "The `buttercup-after-each' function"
+ (it "adds its argument to the after-each list of the current suite"
+ (let* ((suite (make-buttercup-suite))
+ (buttercup--current-suite suite))
+ (buttercup-after-each 23)
+
+ (expect (buttercup-suite-after-each suite)
+ :to-equal
+ (list 23)))))
+
+(describe "The `before-all' macro"
+ (it "expands to a function call"
+ (expect (macroexpand '(before-all (+ 1 1)))
+ :to-equal
+ '(buttercup-before-all (lambda () (+ 1 1))))))
+
+(describe "The `buttercup-before-all' function"
+ (it "adds its argument to the before-all list of the current suite"
+ (let* ((suite (make-buttercup-suite))
+ (buttercup--current-suite suite))
+ (buttercup-before-all 23)
+
+ (expect (buttercup-suite-before-all suite)
+ :to-equal
+ (list 23)))))
+
+(describe "The `after-all' macro"
+ (it "expands to a function call"
+ (expect (macroexpand '(after-all (+ 1 1)))
+ :to-equal
+ '(buttercup-after-all (lambda () (+ 1 1))))))
+
+(describe "The `buttercup-after-all' function"
+ (it "adds its argument to the after-all list of the current suite"
+ (let* ((suite (make-buttercup-suite))
+ (buttercup--current-suite suite))
+ (buttercup-after-all 23)
+
+ (expect (buttercup-suite-after-all suite)
+ :to-equal
+ (list 23)))))
diff --git a/buttercup.el b/buttercup.el
index 6b73a3d..f0b2b7f 100644
--- a/buttercup.el
+++ b/buttercup.el
@@ -232,7 +232,11 @@ MATCHER is either a matcher defined with
(cl-defstruct buttercup-suite
description
- children)
+ children
+ before-each
+ after-each
+ before-all
+ after-all)
(defun buttercup-suite-add-child (parent child)
"Add a CHILD suite to a PARENT suite."
@@ -287,6 +291,46 @@ form.")
:description description
:function body-function)))
+;;;;;;;;;;;;;;;;;;;;;;
+;;; Setup and Teardown
+
+(defmacro before-each (&rest body)
+ (declare (indent 0))
+ `(buttercup-before-each (lambda () ,@body)))
+
+(defun buttercup-before-each (function)
+ (setf (buttercup-suite-before-each buttercup--current-suite)
+ (append (buttercup-suite-before-each buttercup--current-suite)
+ (list function))))
+
+(defmacro after-each (&rest body)
+ (declare (indent 0))
+ `(buttercup-after-each (lambda () ,@body)))
+
+(defun buttercup-after-each (function)
+ (setf (buttercup-suite-after-each buttercup--current-suite)
+ (append (buttercup-suite-after-each buttercup--current-suite)
+ (list function))))
+
+(defmacro before-all (&rest body)
+ (declare (indent 0))
+ `(buttercup-before-all (lambda () ,@body)))
+
+(defun buttercup-before-all (function)
+ (setf (buttercup-suite-before-all buttercup--current-suite)
+ (append (buttercup-suite-before-all buttercup--current-suite)
+ (list function))))
+
+(defmacro after-all (&rest body)
+ (declare (indent 0))
+ `(buttercup-after-all (lambda () ,@body)))
+
+(defun buttercup-after-all (function)
+ (setf (buttercup-suite-after-all buttercup--current-suite)
+ (append (buttercup-suite-after-all buttercup--current-suite)
+ (list function))))
+
+
;; (let* ((buttercup--descriptions (cons description
;; buttercup--descriptions))
;; (debugger (lambda (&rest args)
@@ -326,10 +370,26 @@ form.")
(mapc #'buttercup-run-suite buttercup-suites)
(error "No suites defined")))
+(defvar buttercup--before-each nil
+ "A list of functions to call before each spec.
+
+Do not change the global value.")
+
+(defvar buttercup--after-each nil
+ "A list of functions to call after each spec.
+
+Do not change the global value.")
+
(defun buttercup-run-suite (suite &optional level)
(let* ((level (or level 0))
- (indent (make-string (* 2 level) ?\s)))
+ (indent (make-string (* 2 level) ?\s))
+ (buttercup--before-each (append buttercup--before-each
+ (buttercup-suite-before-each suite)))
+ (buttercup--after-each (append (buttercup-suite-after-each suite)
+ buttercup--after-each)))
(message "%s%s" indent (buttercup-suite-description suite))
+ (dolist (f (buttercup-suite-before-all suite))
+ (funcall f))
(dolist (sub (buttercup-suite-children suite))
(cond
((buttercup-suite-p sub)
@@ -338,9 +398,21 @@ form.")
(message "%s%s"
(make-string (* 2 (1+ level)) ?\s)
(buttercup-spec-description sub))
- (funcall (buttercup-spec-function sub)))))
+ (dolist (f buttercup--before-each)
+ (funcall f))
+ (funcall (buttercup-spec-function sub))
+ (dolist (f buttercup--after-each)
+ (funcall f)))))
+ (dolist (f (buttercup-suite-after-all suite))
+ (funcall f))
(message "")))
+(defun buttercup-run-at-point ()
+ (let ((buttercup-suites nil)
+ (lexical-binding t))
+ (eval-defun nil)
+ (buttercup-run)))
+
(defun buttercup-markdown-runner ()
(let ((lisp-buffer (generate-new-buffer "elisp")))
(dolist (file command-line-args-left)
@@ -352,7 +424,8 @@ form.")
(with-current-buffer lisp-buffer
(insert code))))))
(with-current-buffer lisp-buffer
- (setq lexical-binding t)
+ (setq lexical-binding t
+ debug-on-error t)
(eval-region (point-min)
(point-max)))
(buttercup-run)))
- [nongnu] elpa/buttercup fd70ccf 002/340: Specs are now structs., (continued)
- [nongnu] elpa/buttercup fd70ccf 002/340: Specs are now structs., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup ba40ff8 027/340: Spies: :and-return-value, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 20b87dd 031/340: Refactoring of spy-on to be more concise and DRY., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup bb31c85 043/340: Edebugable., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 5d369cd 007/340: Do not use emacs-snapshot from EVM, that's slow., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 07b1ee6 008/340: Add compatibility definition for define-error, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 7cdb219 028/340: Spies: :and-call-fake, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 5db449f 025/340: Spies, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup d580e5c 011/340: Add build status badge., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 3637e3d 015/340: Add more extensive matcher tests to the README., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup a277b0e 022/340: Setup and teardown: before-each, after-each, before-all, after-all,
ELPA Syncer <=
- [nongnu] elpa/buttercup 1882d6f 013/340: Comment cleanup. Also, make elisp lexically scoped., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup c5a9f26 003/340: Add first set of unit tests., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 36522b9 006/340: Add .travis.yml, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup f44e65d 004/340: Unit tests for define-matcher and apply-matcher., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup d3cd12a 019/340: Tests for the buttercup-describe function, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 3772ad5 014/340: Use eval-region instead of eval-buffer., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 8aa87c8 026/340: Spy :and-call-through, ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 683614d 018/340: Tests for the describe macro., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 0221830 023/340: Remove setup and teardown from ROADMAP.md., ELPA Syncer, 2021/12/16
- [nongnu] elpa/buttercup 695e444 016/340: buttercup-markdown-runner: Use setq, not setq-local., ELPA Syncer, 2021/12/16