Playing Fizzbuzz with property-based testing
Tags: webdev, propertybasedtesting, beginners, clojure
Published: 2022-11-21 10:56:21

---
title: "Playing Fizzbuzz with property-based testing"
published: true
description: "Practising property-based testing with the FizzBuzz kata in Clojure"
canonical_url: https://codesai.com/posts/2019/07/fizzbuzz-pbt
tags: 
  - webdev
  - propertybasedtesting
  - beginners
  - clojure
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9wtdwknvd8t2aa4idf2.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2022-11-20 01:19 +0000
---

<h3>Introduction. </h3>

Lately, I've been playing a bit with property-based testing. 

I practised doing the FizzBuzz kata in Clojure and used the following constraints for fun<a href="#nota1"><sup>[1]</sup></a>:

1. Add one property at a time before writing the code to make the property hold.
2. Make the failing test pass before writing a new property.

<h3>The kata step by step. </h3>

To create the properties, I partitioned the first 100 integers according to how they are transformed by the code. This was very easy using two of the operations on sets that Clojure provides ([difference](https://clojuredocs.org/clojure.set/difference) and [intersection](https://clojuredocs.org/clojure.set/intersection)).

The first property I wrote checks that the **multiples of 3 but not 5 are Fizz**:

```clojure
(ns fizzbuzz-pbt.core-test
  (:require
    [midje.sweet :refer :all]
    [clojure.set :as set]
    [clojure.test.check.generators :as gen]
    [midje.experimental :refer [for-all]]
    [fizzbuzz-pbt.core :as sut]))

(defn- multiples-of [n]
  (iterate #(+ % n) n))

(defn- fizzbuzz-for [n]
  (nth (sut/fizzbuzz) (dec n)))

(def multiples-of-3 
  (set (take-while #(< % 101) (multiples-of 3))))

(def multiples-of-5 
  (set (take-while #(< % 101) (multiples-of 5))))

(facts
  "about fizzbuzz"

  (fact
    "multiples of 3 but not 5 are Fizz"
    (for-all
      [n (gen/elements
           (set/difference
             multiples-of-3
             multiples-of-5))]
      {:num-tests 100}
      (fizzbuzz-for n) => "Fizz")))
```

and this is the code that makes that test pass:

```clojure
(ns fizzbuzz-pbt.core)

(defn fizzbuzz []
  (take 100 (cycle ["" "" "Fizz"])))
```

Next, I wrote a property to check that the **multiples of 5 but not 3 are Buzz** (I show only the new property for brevity):

```clojure
(ns fizzbuzz-pbt.core-test
  (:require
    ;; other requires
    [fizzbuzz-pbt.core :as sut]))

;; ...
;; some helpers
;; ...

(facts
  "about fizzbuzz"
 ;; ...
 ;; previous properties
 ;; ...
  
  (fact
    "multiples of 5 but not 3 are Buzz"
    (for-all
      [n (gen/elements
           (set/difference
             multiples-of-5
             multiples-of-3))]
      {:num-tests 100}
      (fizzbuzz-for n) => "Buzz")))
```

and this is the code that makes the new test pass:

```clojure
(ns fizzbuzz-pbt.core)

(defn fizzbuzz []
  (take 100 (map #(str %1 %2)
                 (cycle ["" "" "Fizz"])
                 (cycle ["" "" "" "" "Buzz"]))))
```

Then, I added a property to check that the **multiples of 3 and 5 are FizzBuzz**:

```clojure
(ns fizzbuzz-pbt.core-test
  (:require
    ;; other requires
    [fizzbuzz-pbt.core :as sut]))

;; ...
;; some helpers
;; ...

(facts
  "about fizzbuzz"

  ;; ...
  ;; previous properties
  ;; ...

  (fact
    "multiples of 3 and 5 are FizzBuzz"
    (for-all
      [n (gen/elements
           (set/intersection
             multiples-of-5
             multiples-of-3))]
      {:num-tests 100}
      (fizzbuzz-for n) => "FizzBuzz")))
```

which was already passing with the existing production code.

Finally, I added a property to check that the **rest of numbers are just casted to a string**:

```clojure
(ns fizzbuzz-pbt.core-test
  (:require
    ;; other requires
    [fizzbuzz-pbt.core :as sut]))

;; ...
;; some helpers
;; ...

(facts
  "about fizzbuzz"

  ;; ...
  ;; previous properties
  ;; ...

  (fact
    "the rest of numbers are casted to string"
    (for-all
      [n (gen/elements
           (set/difference
             (set (range 1 101))
             multiples-of-3
             multiples-of-5))]
      {:num-tests 100}
      (fizzbuzz-for n) => (str n))))
```

which Id made pass with this version of the code:

```clojure
(ns fizzbuzz-pbt.core
  (:require
    [clojure.string :as string]))

(defn fizzbuzz []
  (map #(string/replace (str %1 %2) #"^$" (str %3))
       (cycle ["" "" "Fizz"])
       (cycle ["" "" "" "" "Buzz"])
       (range 1 101)))
```

<h3>The final result. </h3>

These are the resulting tests where you can see all the properties together:

```clojure
(ns fizzbuzz-pbt.core-test
  (:require
    [midje.sweet :refer :all]
    [clojure.set :as set]
    [clojure.test.check.generators :as gen]
    [midje.experimental :refer [for-all]]
    [fizzbuzz-pbt.core :as sut]))

(defn- multiples-of [n]
  (iterate #(+ % n) n))

(defn- fizzbuzz-for [n]
  (nth (sut/fizzbuzz) (dec n)))

(def multiples-of-3 
  (set (take-while #(< % 101) (multiples-of 3))))

(def multiples-of-5 
  (set (take-while #(< % 101) (multiples-of 5))))

(facts
  "about fizzbuzz"

  (fact
    "multiples of 3 but not 5 are Fizz"
    (for-all
      [n (gen/elements
           (set/difference
             multiples-of-3
             multiples-of-5))]
      {:num-tests 100}
      (fizzbuzz-for n) => "Fizz"))

  (fact
    "multiples of 5 but not 3 are Buzz"
    (for-all
      [n (gen/elements
           (set/difference
             multiples-of-5
             multiples-of-3))]
      {:num-tests 100}
      (fizzbuzz-for n) => "Buzz"))

  (fact
    "multiples of 3 and 5 are FizzBuzz"
    (for-all
      [n (gen/elements
           (set/intersection
             multiples-of-5
             multiples-of-3))]
      {:num-tests 100}
      (fizzbuzz-for n) => "FizzBuzz"))

  (fact
    "the rest of numbers are casted to string"
    (for-all
      [n (gen/elements
           (set/difference
             (set (range 1 101))
             multiples-of-3
             multiples-of-5))]
      {:num-tests 100}
      (fizzbuzz-for n) => (str n))))
```

You can find [all the code in this repository](https://github.com/trikitrok/fizzbuzz-pbt).

<h3>Conclusions. </h3>

It was a lot of fun doing this kata. It is a toy example that didn't make me dive a lot into [clojure.check's generators documentation](http://clojure.github.io/test.check/) because I could take advantage of [Clojure's set functions](https://clojure.github.io/clojure/clojure.set-api.html) to write the properties. 

I think the resulting properties are quite readable even if you don't know Clojure. On the other hand, the resulting implementation is probably not similar to the ones you're used to see, and it shows Clojure's conciseness and expressiveness. 

**Footnotes**:

<div class="foot-note">
  <a name="nota1"></a> [1] I'm not saying that you should do property-based testing with this constraints. They probably make no sense in real cases. The constraints were meant to make solving the kata fun.
</div>