emacs-elpa-diffs
[Top][All Lists]
Advanced

[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)))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]