Nico is blogging

My thoughts on software development, clojure, ruby, and trying to deal with complexity in simple ways

Converting test.check to .cljc

After working for more than a month on the conversion of test.chuck to .cljc, I was a bit scared when Gary Fredericks suggested to me that I should do the same in test.check. But that feeling lasted for just 1 second and then I started to think that it would be a nice opportunity to do a bigger contribution to the clojure ecosystem, but also an opportunity to learn more about the test.check codebase.

Let’s step back a bit. I’m a big fan of test.check and generative testing in general. I think I was very lucky for being in Clojure/West 2014 when John Hughes gave his incredible talk Testing the Hard Stuff and Staying Sane. As he was talking, everything started to make sense. That feeling that I always had when writing tests, that some piece was missing, that the way we usually write tests was not enough… well John had the answer for that: Generative Testing.

When I came back to Buenos Aires from San Francisco, I started to play a bit with test.check and also to see what similar tools were available in ruby, which was the language I was using during those days. I won’t extend much on this but just as a tl;dr: IMO the best generative testing library in ruby is rantly, but it’s still too far from what test.check offers. I talked about rantly in a Ruby Meetup and also in clojure meetups in Sao Paulo and Buenos Aires, so if you happen to walk close to me there’s a high chance I will be talking about how we should do more property testing and not only testing by example in our codebases.

Reader conditionals (.cljc) in test.check

Today I finished what I think is the complete conversion of the test.check codebase to .cljc. I left the part about random number generators out of the conversion because it’s very different from clj to cljs, so having all that code in a single file doesn’t make much sense. And I’m saying “what I think is the complete conversion” because I just uploaded the patches to TCHECK-79 and now the review process will start and anything might happen, from having to do small changes to having the ticket rejected altogether. If that happens, sorry for all this buzz, I will have enjoyed the journey anyways…

Now, why do we even care about .cljc files and Reader Conditionals??

You might be wondering what .cljc is. Well, .cljc is the extension of the files that want to take advantage of the (relatively) new Reader Conditionals in clojure.

Reader conditionals allow us to share code of multiple clojure dialects (Clojure, ClojureScript, ClojureCLR) in a single file that will be compiled to the corresponding platforms. The main advantage of doing so is that a lot of duplication can be avoided.

Let’s see a small example:

  #?(:clj  (java.util.Date.)
     :cljs (js/Date.)))

This is a very small code sample, but take a look to the commit that converts the generators namespace to .cljc. I know, you won’t click on that link, so here’s a screenshot:

generators.cljc 50 additions and 715 deletions

Yes, that’s 50 lines of code added and 715 deleted!. It’s a lot of duplication that can be avoided thanks to this.

For more info on Reader Conditionals see the design document about reader conditionals in, The Reader - Reader Conditionals in, Clojure Reader Conditionals by Example by Daniel Compton and Portable Macro Musing by Mike Fikes.

Want to help, or just try it out?

You can help by playing around with the converted code. You can get it by applying the 10 patches from the ticket, or by cloning my fork of test.check and checking out the cljc branch. Cloning the repo will be probably easier than applying the patches :), but YMMV.

Feel free to add comments to the ticket, if you think you have anything to add, that would be of great help!

I’d like to thank Gary Fredericks for all the work he has done in test.chuck and test.check, for being very receptive to contributions on both projects, and also for the great support he gave me while trying to do all this stuff.