chickadee » missbehave

Missbehave

Introduction

Missbehave is a toolkit that allows to you to bring behaviour driven development in the spirit of rspec, to your scheme of choice.

It is very much inspired and mostly modeled after the famous rspec-library.

Sources

You can find the egg sources at: https://bitbucket.org/certainty/missbehave

The name

As it has been asked on #chicken and it really can be misleading, here is why the name is spelled this way. It is intended to be a pun. (I guess not a good one, as i have to explain it)

It consists of the words miss where i mean error. And behave where i mean, erm, behave. So you may read as "C'mon miss, behave!". That's by the way also the reason why the cli is called behave.

So the two words together form "missbehave" which is intentionally close to "misbehave".

To be totally fair. I quite understand why this has been discussed, as i'm very aware of my limitions regarding english spelling.

Examples

(use missbehave missbehave-matchers missbehave-stubs miscmacros)

(define (callee . args) 'called)
(define (call-it n)
  (repeat n (callee)))

(describe "Missbehave features"
  (describe "implicit subject"
     (subject-set! 42)
     (it (is > 10))
     (it (is a number))
     (it should (be 42)))
  
         
  (describe "Simple be matchers"
     (it "must be true"
         (expect #t (to (be true))))
     (it "must be a string"
         (expect "hello" (to (be a string))))
     (it "can use standard operators"
         (expect 3 (is > 0)))
     (it "can use predicates"
         (expect '() (is null?))))

  
  (define (numbers ls) ls)
  
  (describe "Have"
    (it "can be used with a collection procedure"
      (expect '(1 2 3) (has 3 numbers)))

    (it "supports arbritary sugar"
      (expect "foo" (has 3 characters))))

  (describe "Matches string"
    (it "checks if regex matches string"
      (expect '(: (+ digit)) (matches-string "1234")))
    (it "checks the submatches"
      (expect '(: (+ digit) (submatch (+ any))) (matches-string "1234foo" with-matches: '((1 . "foo"))))))

  (describe "Pending"
     (it "is implicitly pending")
     (it "is explicitly pending"
         (pending)
         (expect '() (be a number))))         

  (describe "Procedure expectations"
    (context "Application-count"
      (it "checks application count"
          (expect (call-it 1) (call callee once)))
      (it "checks application count > 1"
          (expect (call-it 4) (call callee (4 times))))
      (it "checks for no calls"
          (expect (+ 1 1) (call callee never))))
    (context "Arguments"
     (it "checks arguments"
         (expect (callee 1 2) (call callee (with 1 2))))
     (it "mixes arguments and application count"
         (expect (begin (callee 1 2) (callee 1 2)) (call callee (with 1 2) twice)))))


  (describe "Procedure stubs"
    (it "can stub return values"
        (stub! callee (returns 'not-called))
        (expect (callee) (be 'not-called)))

    (it "provides temporary stubs"
        (let ((proc (lambda () 'test)))
          (expect (proc) (be 'test))
          (with-stubs! ((proc (returns 'passed)))
            (expect (proc) (be 'passed)))
          (expect (proc) (be 'test))))))

Now invoke it with:

$ behave test-spec.scm

Produces the following output:

Missbehave features
Procedure stubs
  It provides temporary stubs
  It can stub return values
Procedure expectations
Arguments
  It mixes arguments and application count
  It checks arguments
Application-count
  It checks for no calls
  It checks application count > 1
  It checks application count
Pending
  [P] It is explicitly pending
  [P] It is implicitly pending
Simple be matchers
  It can use predicates
  It can use standard operators
  It must be a string
  It must be true
implicit subject
  It should (be a number)
  It should (be 42)


Total: 15 Successful: 13 Pending: 2 Failures: 0

Authors

David Krentzlin

Api

Contexts

Contexts are a way to group your examples. You can use the the macro context and its alias describe to create them.

(context context-name examples ...) syntax

Create a context with the name context-name.

(context context-name (meta (tags ...)) examples ...) syntax

Create a context with name context-name and mark them with tags. This can be used to filter examples using the --tags command-line switch of behave.

(context context-name (meta ((tag . value) ...)) examples ...) syntax

Create a context with name context-name and mark them with the key-value pairs. Those can be used to attach data and to filter them using the --tags command-line-switch of behave

Examples
(context "A simple context" 
  (it "does nothing"))

(context "A context with tags" (meta (long-running verbose))
  (it "is pending"))

(context "A context with value tags" (meta ((bug . "#402")))
  (it "is pending"))
  

Examples

Examples allow you to specify a specific behaviour of your subject.

(it description) syntax

Create a pending example with the description set to description

(it should matcher) syntax

Creates an example that executes matcher on the current subject. See also subject-set!.

(it description expectation ...) syntax

Creates an examples with the description set to description and the expectations.

(it "is pending")

(it "succeeds"
  (expect #t))
Meta-Information

Metainformation can be used to mark certain examples or provide some form of addtional information. As a plus the behave-cli allows you to use those meta-information to filter certain examples.

(it description (meta (tags ...)) expectations) syntax

Creates an example with expectations and tags.

(it description (meta ((tag . value))) expectations ...) syntax

Create an example with expectations and key-value pairs, so that it can be filtered.

Examples
(it "has tags" (meta (some tag))
  (expect #t))

(it "has more tags" (meta ((some . tag) (some . other)))
  (expect #t))

Expectations

Expectations are the bdd-way of stating assertions.

(expect form) syntax

Create an expection that evaluates form and will fail if form evaluates to #f and pass otherwise.

(it "succeeds")
  (expect (string? "chickumber")))

(it "fails" )
  (expect (> 2 3)))
(expect subject to (call arguments ...)) syntax

Creates an application-expectation. See also procedure-expections.

(define (reverse-list ls)
  (reverse ls))

(expect (reverse-list (list 1 2 3)) (call reverse)))
(expect (reverse-list (list 1 2)) (call reverse once)))
(expect (reverse-list (list 1 2)) (call reverse (with (list 1 2))))
(expect (reverse-list (list 1 2)) (call reverse (with (list 1 2)) once))
(expect subject matcher) syntax

Create an expection on subject with matcher matcher.

(expect (list 1 2) (be a list))
(expect "foobar" (have 6 characters))
(expect 3.1 (be (close-to 3)))
Negative Expectations

If you want to state that a given subject should not have a certain behaviou, you can simply wrap the expect-form into a do-not form

(do-not (expect ....)) syntax

Creates an expection that fails if its inner expectation succeeds and vice versa.

Matchers

Matchers are the way to implement the checks for a given behaviour. They verify that a given subject behaves as expected.

(matcher (check (subject) check-code) (message (form subject negate) message-code)) syntax

This creates a fresh matcher object. You need to implement both the check and the message.

  • check The check is a procedure that receives one argument, the subject. The subject is a promise so you need to force it if you want to retrieve the value.
  • message This is the procedure that is invoked to generate the message. It receives three arguments
    • form This is the quoted form that was passed to expect
    • subject This is the subject. Again this is a promise so you need to force it to retrieve the actual value.
    • negate Indicates if the check shall be negated. This is set to true if the expect-form has been wrapped into a (do-not) form.
(define (greater-than x)
 (matcher 
   (check (subject)
     (> (force subject) x))
   (message (form subject negate)
     (if negate
        (sprintf "Expected ~A to be less than or equal to ~A" (force subject) x)
        (sprintf "Expected ~A to be greater than ~A" (force subject) x)))))

(expect 10 (be (greater-than 5)))

Builtin Matchers

Be

The be matcher is possibly the most used. It allows you to describe what a given subject should be. The be macro allows a fair few variations. I've listed all possible forms below

Aliases: is

(be true) syntax

matches if subject is #t

(be false) syntax

matches if subject is #f

(be a type) syntax

matches if subject is of the specified type. It assumes that there is a procedure type? to check that.

(expect "string" (be a string))
(expect (list 1 2 3) (be a list))
(be pred?) syntax

matches if pred? evaluates to #t when it is applied to subject

(expect 0 (be zero?))
(be value) syntax

matches if current subject is equal to value in the sense of equal?.

(be pred? value ...) syntax

This expands into something like (apply pred? (list subject value ...)). It provides a handy notation to write curried checks.

(expect 2 (be > 0))
(expect -100 (be <=  10))
Be-helpers

There are some procedures that enhance the possibilities of the be matcher

(close-to value #!key (delta 0.3)) procedure
(expect 2 (be (close-to 1))
(expect  10/3 (be (close-to 3 delta: 0.4))
(any-of value . more-values) procedure
(expect 2 (be (any-of 1 2 3 4))
(none-of value . more-values) procedure
(expect 5 (be (none-of 1 2 3 4))
(list-including value . more-values) procedure
(expect (list 1 2 3) (be (list-including 1 2))
match-string

This matcher can be used to describe the behaviour of regular expressions.

Aliases: matches-string

(match-string str #!key (with-matches #f)) procedure

This succeeds if the regular expression represented by subject matches the string str. If with-matches is supplied, it is expected to be an alist holding the expected captures.

(expect '(: (+ space) (submatch (+ any))) 
  (match-string "    test" with-matches: '((1 . "test")))

(expect '(: (+ any)) (match-string "just a test"))
have

This matcher can be used to specify expectations on the amount of items in a collection.

Aliases: has

(expect "string" (has 6 chars))

(define (even-numbers ls) (filter even? ls))

(expect '(1 2 3 4 5) (has 2 even-numbers))
call

This matcher is used to express procedure-expectations. It allows you to state that a form is expected to call a procedure.

Aliases: calls

(call proc once) syntax

matches if proc is called exactly once.

(call proc twice) syntax

matches if proc is called twice

(call proc never) syntax

matches if proc is never called.

(call proc (n time)) syntax

matches if proc is called n times.

(call proc (with arg ...)) syntax

matches if proc is called at least once with the given arguments.

(call proc (with arg ...) amount-spec) syntax

matches if proc is called with the given arguments and matches the amount-spec which is one of the above (once never (n times)).

(define (foo) "foo")
(define (i-call-foo) (foo))

(define (bar x y) "bar")
(define (i-call-bar) (bar 1 2))

(expect (i-call-foo) (call foo once))
(expect (i-call-bar) (call bar (with 1 2) once))
raise

This matcher allows you to formulate expections about errors

(raise error) syntax

Matches if subject raises errors.

(raise (kind ...)) syntax

Matches if subject raises an error and sets the kind-property at least to those specified.

(expect (error "test") (raise error))

Stubs

Missbehave provides a simple way to stub procedures. This is useful in cases where you want to control some of the procedures that interface, with the one you currently describe.

(with-stubs! ((name stub) ... ) code ...) syntax

This form replaces the behavior of name with the stub code. Note that there is a (returns)-form which is a short-hand to create a procedure that returns the given value.

(returns value . more-values) procedure

Creates a procedure that returns value. If you specify multiple value the procedure will return multiple values.

(define (return-3) 3)
(with-stubs! ((return-3 (returns "not 3"))
  (printf "return-3 returns ~A~%" (return-3)))

Hooks

Contents »