On the importance of testing for noobs - or 'there's no such thing as a stupid test'.

45 views
Skip to first unread message

Dexter Lagan

unread,
May 3, 2020, 7:56:27 AM5/3/20
to Racket Users
  If it wasn't abundantly clear yet, I'm a Racket noob, so when I had this kind of thing in my code:

(define x 'a)
(case x
  (('a) return-something)
  (('b) return-something-else)
  (('c) return-yet-another-thing))

  I was baffled to find out that the case form always returned void (thanks Alex).
I made this mistake a few times in the past: quoting symbols in case forms. I'm human, humans make errors, and I make a LOT of errors.
  I wrote the case form out of habit. I quoted symbols out of habit. I didn't test the proc in the REPL, because well, I was quite sure how case worked. The same habit pushed me to write a test. But I thought... bah this is just too simple, why bother? Yet I should have, because it would have instantly showed me that my expectations were off.

(module+ test
  (require rackunit))
...
(define (bad-case x)
  (case x
    (('a) return-something)
    (('b) return-something-else)
    (('c) return-yet-another-thing)))
; unit test
(module+ test
  (check-equal? (bad-case 'b) 'b))
; test fails, bad-case returns void.

Obviously the correct syntax is:

(case x
    ((a) return-something)
    ((b) return-something-else)
    ((c) return-yet-another-thing))

  That very basic mistake made me spend quite a few hours tracking where that void was coming from. And when that void pipes through a few functions before landing in a find-files predicate, and that the error doesn't quite mentions which parameter it came from...

  I read somewhere, that we should use plenty of asserts in our code. I usually have unit tests for most of my functions, except on the ones I find trivial. Silly mistake.
  So uh.. if you're a noob like me, write lots of tests. Test your functions in the REPL. We say 'there's no such thing as a stupid question'. I say, there's no such thing as a stupid test. Best of all, tests are free with Racket!

Dex

Simon Schlee

unread,
May 4, 2020, 7:19:13 AM5/4/20
to Racket Users
And use contracts.
I can't claim that all my code uses contracts everywhere, but when I encounter unexpected/bewildering behavior I add contracts to break those chains of "piped functions".
You can start with define/contract for ease of use.

Later on you can put functions that belong together in one module and test that they work as expected.
Then use (provide (contract-out ...)) to put contracts on the exposed functions, structs, etc. to make sure that they can't be used with unexpected values from the outside.
This way your module can have internal invariants which can't be broken from the outside.

Simon

Dexter Lagan

unread,
May 4, 2020, 7:20:30 AM5/4/20
to Simon Schlee, Racket Users
  Yes! Haven’t played with contracts yet, but they’re next in my list.

Cheers,

Dex

On May 4, 2020, at 1:19 PM, Simon Schlee <schle...@gmail.com> wrote:


--
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.
To view this discussion on the web visit https://groups.google.com/d/msgid/racket-users/f2bd097c-5718-4ead-b497-7703942af12b%40googlegroups.com.
Reply all
Reply to author
Forward
0 new messages