This is more examples than talk.
Add ScalaCheck to your build.sbt file
First Problem, FizzBuzz
Let’s look at some tests for FizzBuzz problem.
Each test is trying to prove certain property of FizzBuzz, for instance, property “only div by 3” is trying to prove that Fizz is only returned when the passed value is divisible by 3 and not by 5. Taking a look at the value generator (divBy3) will allow us to realized we are filtering out those that are divisible by 5.
Following the same like of thinking we can prove other properties out such as only those that are divisible by 5 and not by 3 should be translated to “Buzz”, or if the value is not divisible by 3 and by 5 it should be translated using the id function and toString, so they become themselves in string format.
The magic here is that each test will run at least 100 times with a randomized value space (that is not stable) based on our generator properties. This is, in my opinion, stronger that testing for using limited values of the problem domain, such as 5, 10, 15, 2, 24, 2.
On the other hand, this testing techniques should not replace TDD but rather extend it for more complete test suite.
Our implementation of FizzBuzz looks like this.
Second Problem, Stack
Now, let’s see how we can use the same technique to test a custom, yet functional stack implementation.
Let’s start by the tests since they are our area of focus in the post.
First, we define a value generator, specifically for positives int values.
Then we define our first test that push a number of values (in order) to the stack and verify that the size of the stack corresponds to the number of values that were pushed.
Remember, this same test will run 100 times with a different number of values to be pushed. The first time it runs might be pushing 2 values and verifying that the size is 2 and then it might run again and pushes 97 values and again verifies that the size of the stack is 97.
Our second test verifies that pick from the stack does not modify it.
Using the same principle, the test runs a lot of times, pushing a different number of items into the stack every time and then calling
.head (pick) and then verifying that the size is equals to the number of values that were pushed, which means
.head does not modify the stack.
Now, we can test getting a list representation of the stack (does not modify the stack).
Once more, using the same principle, each time the test runs, it checks that
.toList returns the same values that we pushed in reversed order.
Push & Pull
Our “push pull” test verifies that whatever we pushed into the stack can be pulled out in the right order. Again, this test will run 100 times with different stack sizes.
Our Stack is an inmutable data structure. Operations on the stack does not modify it but rather create new stacks.
Property-based testing is a powerful tool to have in your side. I have found it very useful when writing complex data structure like the ones we have been adding to Dogs. However, it should not replace other techniques such as TDD (Test Driven Development) but rather complement them.