I think it would be more (Racket-)idiomatic to make account a parameter (as in make-parameter) and have something that can update the parameter around a test case. Here is my preferred extension, by example:

(define account (make-parameter #f))

(define (call/open-bank proc)
  (parameterize ((account (open-bank))) (proc)))

(test-suite "tests"
  #:around-each call/open-bank
  (test-case "test" (check-= (balance (account)) 0))
  ....)

And #:before-each can be implemented given #:around-each, of course, but around is more general. For example, I have written many tests recently where I create a custodian for each test case and shut it down at the end.

BTW, I dislike the idea of #:before-each delaying the evaluation of its expression; just make it strict and take a procedure.

All that said, I think it's easy enough to create a helper function or macro and use it in each test case, so I don't think there's a pressing need to extend rackunit (inside of which there is a smaller testing framework struggling to get out, of course).

Ryan


On 03/09/2016 11:34 AM, Brian Adkins wrote:
If you're going to add something, please don't do #1 :) That's how it's
done in Ruby, but it's an OO, stateful platform, so it fits.

The only other way I've seen recently is the Elixir version (
http://elixir-lang.org/docs/stable/ex_unit/ExUnit.Case.html) , and I
like it - the setup function simply returns a value that is passed to
the test case. Your example confused me because I think you used balance
instead of account as the param e.g.:

(test-suite #:before-each (open-bank)
     (test-case/param (lambda (account) (check-= (balance account 0)))
      ...))

I would also want to retain the string description.

I agree that divergence can be a problem, but I'm a little less
concerned with that in something like Rackunit, so I'm on the fence on
this now given the simplicity of the solution for my personal need.

The Elixir solution probably uses pattern matching on the test function,
so there's a version that accepts a string description and a
block/lambda, and another version that accepts a string, a hash and a
block/lambda. This saves you from having to introduce a #:param keyword.

Maybe something like:

(test-suite #:before-each (open-bank)
   (test-case/context "Description" account
     (check-= (balance account 0))))

On Mar 9, 2016, at 11:06 AM, Sam Tobin-Hochstadt <sa...@cs.indiana.edu
<mailto:sa...@cs.indiana.edu>> wrote:

I think having a built-in way to do something like this would be a
good thing, rather than having everyone write their own abstraction
for this. One way we've avoided divergence in the Racket community
despite Racket's flexibility is by building in the right abstractions
in core libraries. But I share Jay's worry about the code in your
gist.

Here are a few possible ways I think we could add per-test-case setup
code:

1. Purely state-based. Add a `#:before-each` keyword, which defines an
expression to run before each test case. Then you'd write:

(let ([account #f])
 (test-suite #:before-each (set! account (open-bank))
    (test-case (check-= (balance account 0))
     ...)))

2. Parameter-passing, with an explicit lambda (could maybe make this a
keyword version of test-case as well):

(test-suite #:before-each (open-bank)
    (test-case/param (lambda (balance) (check-= (balance account 0)))
     ...))

3. Parameter-passing, with a binding position in the macro (perhaps
using a keyword like #:param):

(test-suite #:before-each (open-bank)
    (test-case #:param balance (check-= (balance account 0))
     ...))

Certainly 1 is the simplest to implement, but stateful test frameworks
seem quite unfortunate. 2 is also simple, but has a proliferation of
API surface.

Any other thoughts on which of these is preferable? I'm happy to
implement whichever solution seems best.

Sam


On Wed, Mar 9, 2016 at 10:53 AM, Jay McCarthy <jay.mccar...@gmail.com
<mailto:jay.mccar...@gmail.com>> wrote:
I looked at the gist you posted. I don't think it is plausible for us
to add
something like that to the test code, because you're imagining
splicing the
test body after the before exprs (because in your example, the before
binds
an identifier.)

I really think you just want a slightly different test-case macro
that does
exactly what you want.

(define-syntax-rule (brian-test-case label account . exprs)
(test-case label (define account (open-bank)) . exprs))

I really can't see the appeal of trying to push something into
Rackunit that
can expand to what your example would do. It just seems so much more
complicated to implement and explain what it means than just putting in a
one line macro like this.

I feel that there is an analogy between `do`, `for`, and `let loop`.
It is
definitely wise to not have to program every `for` loop explicitly
with `let
loop`s, but trying to keep expanding the `for` macro to do more and more
towards the Lisp `do` macro is a bad move, IMHO, because it is so
much more
complicated and non-orthogonal. It's better to make a test system that
integrates well with the rest of the language and to just use the
language's
features for extensibility.

Jay


On Tue, Mar 8, 2016 at 1:35 PM, Brian Adkins <lojicdot...@gmail.com
<mailto:lojicdot...@gmail.com>> wrote:

Not exactly. I'm looking for a way to run a function before *each*, of
possibly many, test-cases. The test-suite #:before only runs once before
running all the test-cases.

Although my gist: https://gist.github.com/lojic/db7016fb95b1c05e4ade
only
has a few test-cases, if there were many, the redundancy of
specifying the
context each time would be annoying.

What I'd really like is something similar to Elixir though, so I think
I'll code up that macro/function. It would be more functional to
have each
test-case accept a context parameter, so you'd define a setup
function that
returns a value that each test-case accepts as input.

It might look something like:

(test-suite
 "my test suite"
 #:setup (lambda () (open-bank))

 (test-case "initial balance is 0" account
   (check-eq? (balance account) 0))

 ...

So, the lambda specified in #:setup returns an object, account, that is
passed to each test-case.

I'll need to think of a reasonable syntax for it, but the idea is that
each test-case in a suite will have context created for it functionally.


On Tuesday, March 8, 2016 at 1:17:53 PM UTC-5, Matthias Felleisen wrote:
Are you looking for something like this:

#lang racket

(require rackunit rackunit/text-ui)

(define my-database #f)

(define my-first-test-suite
 (test-suite
  "An example suite"
  #:before (lambda () (set! my-database '(a b c)) (displayln `(Before
,my-database)))
  #:after  (lambda () (set! my-database #f)       (displayln `(Before
,my-database)))
  (test-case
   "An example test"
   (check-eq? 1 1))
  (test-suite "A nested test suite"
              (test-case "Another test"
                         (check-equal? 1 2)))))


Welcome to DrRacket, version 6.4.0.13--2016-03-04(-/f) [3m].
Language: racket.
(run-tests my-first-test-suite)
(Before (a b c))
--------------------
An example suite > A nested test suite > Another test
Another test
FAILURE
name:       check-equal?
location:   unsaved-editor:17:26
actual:     1
expected:   2
. Check failure
--------------------
(Before #f)
1 success(es) 1 failure(s) 0 error(s) 2 test(s) run
1



On Mar 8, 2016, at 1:14 PM, Brian Adkins wrote:

Jay:

Here's a gist: https://gist.github.com/lojic/db7016fb95b1c05e4ade

without.rkt is how I coded it up and with.rkt is how I'd like to be
able to code it.

I agree that it's trivial to add, but for something as common as
"setup" and "teardown" for unit testing, there may be an advantage
to having
it in RackUnit. When it comes down to it, test-case, check-equal, etc.
aren't hard to add either, but why should we all implement those.

Brian

On Tuesday, March 8, 2016 at 12:49:21 PM UTC-5, Jay McCarthy wrote:
Hi Brian,


Can you explain what you want to write? Just imagine that the feature
was already there... what do you want?


I think of Rackunit as a way of writing checks, so if I wanted to do
what you're talking about, then I'd define the macro/function
that you don't
want to. In other words, with Rackunit, you are just writing a
program and
it provides a way to keep track of and print out individual
tests, so all
the features of Racket are there for writing that program.
There's no reason
to add functions, dynamic-wind, and begin blocks to Rackunit, because
they're already in Racket. For instance, my tests look like


(define f ....)
(module+ test
(check-equal? (f ....) ....)
(check-equal? (f ....) ....)
(check-equal? (f ....) ....))



and sometimes


(define (f ....)
(module+ test
(define (test-f-like-woah ....)
  .... (check-equal? (f ....) ....) ....)
(test-f-like-woah ....)
(test-f-like-woah ....))



If I had already done the second kind, then I'd put setup in there.


Given that Rackunit doesn't already have an "outer" paren, each check
really is atomic, so any setup would be environmental, like
wrapping in a
dynamic-wind/parameterize or just another function call before the
check-equal?.


Now, if you REALLY want this, and I don't feel that you should, you
could look at current-check-around and do something like:


(let ([old (current-check-around)])
(parameterize ([current-check-around (lambda (c) (before!) (old c)
(after!))])
 (check-equal? ....)
 (check-equal? ....)

 (check-equal? ....)))



This would run (before!) 3 times and (after!) 3 times.


Jay




On Tue, Mar 8, 2016 at 12:23 PM, Brian Adkins wrote:
Does RackUnit provide a facility similar to the setup & teardown
functions of other unit testing frameworks? In other words, I'd
like to
execute some code before each test w/o coding each test to call a
setup
function or having to create my own macro given how common this is.



As far as I can tell, neither the #:before of test-suite nor the
before macro accomplish this. In fact, I haven't been able to
discern the
purpose of the before macro because the following seem equivalent
to me:



(before

 before-expr

 expr1

 expr2)



before-expr

expr1

expr2



At least with the after macro, there is a guarantee that the
after-expr will be evaluated in the face of exceptions.



Also, as I mentioned in IRC, I just discovered that the setup
function in Elixir (the love child of Erlang and Ruby :) returns an
expression that each test accepts as input. This is more
functional than the
traditional approach of the setup function mutating a variable
that the test
cases access and allows the tests to run concurrently in the same
file.
Might be worth adding to a wishlist for future versions of RackUnit.



Thanks,

Brian



--

You received this message because you are subscribed to the Google
Groups "Racket Users" group.

To unsubscribe from this group and stop receiving emails from it,
send an email to racket-users...@googlegroups.com
<http://googlegroups.com>.

For more options, visit https://groups.google.com/d/optout.





--

Jay McCarthy
Associate Professor
PLT @ CS @ UMass Lowell
http://jeapostrophe.github.io

          "Wherefore, be not weary in well-doing,
     for ye are laying the foundation of a great work.
And out of small things proceedeth that which is great."
                         - D&C 64:33

--
You received this message because you are subscribed to the Google
Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to racket-users+unsubscr...@googlegroups.com
<mailto:racket-users+unsubscr...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it,
send an
email to racket-users+unsubscr...@googlegroups.com
<mailto:racket-users+unsubscr...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.




--
Jay McCarthy
Associate Professor
PLT @ CS @ UMass Lowell
http://jeapostrophe.github.io

          "Wherefore, be not weary in well-doing,
     for ye are laying the foundation of a great work.
And out of small things proceedeth that which is great."
                         - D&C 64:33

--
You received this message because you are subscribed to the Google Groups
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to racket-users+unsubscr...@googlegroups.com
<mailto:racket-users+unsubscr...@googlegroups.com>.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Racket 
Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to