Last December, I wrote about using F#’s interactive REPL to facilitate TDD. Since then, enough interesting developments have happened that I think the topic is worth revisiting.
REPLs are great, but sometimes the “Print” step can leave a little to be desired. F# has pretty good default REPL formatting for records, lists, etc., but not necessarily for other types, like traditional classes. You can add your own REPL format function for a type, but you’re still limited to string output. For large data structures, being able to expand and collapse the output or view a summary would be very helpful.
Luckily, just such a capability was recently added to the F# REPL–provided you are using the Atom editor. The Ionide F# plugin for Atom was updated to make it support rendering objects to the F# interactive output window as HTML. Basically, instead of providing a formatting function of (YourType -> string) where the string is raw output to display, you provide a function of (YourType -> string) where the output is presumed to be HTML.
Better-looking Test Results
The update to Ionide helped me address my previously stated goal of creating prettier test output. Since then, I’ve updated my previous TDD script to dramatically increase the ease of reviewing test output. I’ve gone from simple text output that is all in the same color and size to this:
In the updated script, I’ve built very lightweight data structures that resemble what a test runner would normally do. Then I turned those structures into some simple HTML and CSS. Eventually, it would be awesome to leverage an existing test runner’s HTML output, but that’s a project for another day.
I’ve created a Github repo for others who would like to use my TDD script for their F# development.
I considered creating a NuGet package for these scripts, but I suspect the desire for consumers to tweak the code will be high enough that just copying or forking the code will be easier. Because there’s nothing to compile, you could alternatively reference the project as a Git submodule to easily get the latest updates.
Choice of Test Framework
I also have an update on one of my caveats from last time. Previously, I was experiencing crashes with the xUnit console runner on Mono. However, because the NUnit assertions do not work without running through the NUnit framework, I had to use xUnit to do TDD in the F# REPL. Even though FsUnit mostly papered over those differences, it still really bothered me.
Lately, though, I discovered the source of my crashes, and it is easy to work around. If you load a project into the console runner, which is of type Exe and not Library, you will occasionally get random crashes. So I just changed my main project to a Library and gave it a thin, untested executable wrapper.
Pretty and Fast
I love being able to change code in a compiled language and see easy-to-read test output instantaneously. The best part is how well it scales. You don’t pay the penalty for compiling the entire project to run one test—or even the cost of starting up the runtime. You only have to send a chunk of the script into an already-running instance of the runtime.
I’d like to see the Ionide changes make their way over to VS Code. Because VS Code has decent F# debugging and a halfway decent Vim plugin, it is within striking distance of being a one-stop shop for F# development for me on OS X.
I’d also like to try and integrate the output formatters for existing test frameworks when possible. xUnit is always going to know how to format their results better than I do. The trick will be getting their formatting code to cooperate in an environment that’s nothing like their test runner.
Being able to make incremental but significant progress in tooling—as I hope I have done by prettifying the output of REPL-based TDD—highlights one of my favorite things about open source software communities. Any contributor can move the ball forward more-or-less independently, and everyone reaps the benefits. Where do you want to make your next contribution to improving the process and practice of making software?