3

As a developer who has primarily been doing functional programming in F# for the last several years, I'm very attached to my REPL and use it run my code as I go, testing and refining each function as I write it. However, one area where I often fall short as a developer is in creating good unit tests for every function and submitting them with my initial code-review request.

Other developers on my team who come from a more OOP/C# background have a workflow that is less REPL-driven and use unit tests (MSTEST) to do the experimental testing that I do in the REPL. While this leaves them with a bunch of unit tests at the end of their cycle, I still prefer the REPL, because I can iterate and test much more quickly, without having to build the project and wait for the test-engine each time.

Since we are an F# team, I believe the REPL is the right tool for experimental testing during development, but I also believe we need to have formal unit tests that are executed during builds as part of our gated check-in process. I also don't especially like that since we have to create test classes and test methods for the unit tests, since we don't really use classes very often anywhere else, but I can deal with that. The value of having a repeatable set of tests run on every build to immediately point out any breaking changes is useful enough to deal with the idiosyncrasies of an OOP testing framework.

In doing some research, I have seen things like the Cursive IDE for Clojure which combines a REPL with the clojure.test framework so that the code you write in the REPL can become the unit tests. This seems like an ideal solution, and it looks like the F# testing project Expecto might partially support this pattern, so I'm considering making a proposal to try it out. However, I don't want to introduce a disruptive change to the workflow and testing process unless it's the best available solution.

Are there any simple workflow changes I've overlooked that could resolve this without a change in testing tools? How are REPLs and Unit Tests used together on other large FP-based projects?

4

Read-Eval-Print Loop (REPL) development should be concerned with "growing" usable functions. Unit testing should be concerned with testing those functions to make sure they work properly. There's nothing fundamentally at odds with these two ideas, other than the additional work required to write the unit tests, which is probably something you should be doing anyway.

There have always been two ways to write unit tests: either before or after you write the code under test. Test-Driven Development, which is traditionally Test First, is probably going to eliminate any advantages your REPL gives you, because now you'll be writing unit tests for each of your intermediate REPL steps, which is going to slow you down.

So here is what I would recommend. Write unit tests after you write your functions. Figure out what your function is supposed to do (i.e. create a specification for each function), write the function using your REPL techniques, and then wrap it in unit tests according to your specification. In other words, the unit tests should demonstrate that the function meets its specification, its stated requirements.

This provides you with the benefits of unit testing without getting in the way of the rapid development that your REPL provides, with minimal changes to your current workflow.

Of course, nothing prevents you from using Expecto in the same way, using the same workflow I just described. Expecto appears to have a number of benefits that MSTest does not provide, like performance testing, stress-testing capabilities, the ability to support multiple cores, and tests that don't require classes.

open Expecto

let tests =
  test "A simple test" {
    let subject = "Hello World"
    Expect.equal subject "Hello World" "The strings should equal"
  }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.