One of the most salient features of our Tech Hiring culture is that there is so much bullshit. Everyone knows this. Each of us contributes his share. But we tend to take the situation for granted.
So I would throw in properties based testing, here with kotlin test
importio.kotlintest.specs.FreeSpecimportio.kotlintest.specs.StringSpecfunevilFizzBuzz(number:Int):String=when{number>100->"FOOBAR"number%3==0&&number%5==0->"FizzBuzz"number%3==0->"Fizz"number%5==0->"Buzz"else->number.toString()}funrealFizzBuzz(number:Int):String=when{number%3==0&&number%5==0->"FizzBuzz"number%3==0->"Fizz"number%5==0->"Buzz"else->number.toString()}classFizzBuzzTesting:FreeSpec(){init{"it's always possible to trumps example-based testing"{valexpected=listOf("FizzBuzz","1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz","16","17","Fizz","19","Buzz","Fizz","22","23","Fizz","Buzz","26","Fizz","28","29","FizzBuzz","31")for(iin1..expected.lastIndex){evilFizzBuzz(i)shouldBeexpected[i]}}"but no implementation can break properties-based testing"-{valfizzbuzz=::realFizzBuzz// val fizzbuzz = ::evilFizzBuzz"Multiples of 15"{forAll<Int>{i->if(Math.abs(i)>=Integer.MAX_VALUE/15-1)return@forAlltruevalnb=15*Math.abs(i)fizzbuzz(nb)=="FizzBuzz"}}"Multiples of 3"{forAll<Int>{i->if(Math.abs(i)>=Integer.MAX_VALUE/3-1)return@forAlltruevalnb=3*Math.abs(i)nb%5==0||fizzbuzz(nb)=="Fizz"}}"Multiples of 5"{forAll<Int>{i->if(Math.abs(i)>=Integer.MAX_VALUE/5-1)return@forAlltruevalnb=5*Math.abs(i)nb%5==0||fizzbuzz(nb)=="Fizz"}}"Others"{forAll<Int>{i->valnb=Math.abs(i)nb%5==0||nb%3==0||fizzbuzz(nb)==nb.toString()}}}}}
In Test-Driven Development, you pass the failing test with the simplest possible solution. And your solution has a higher complexity than the one proposed in the article.
You have one edge case handler more (additional “if” statement).
Therefore, if TDD practiced properly, such “evil” solution will not be written.
Anyways.
I agree that PBT has a higher degree of correctness when it comes to testing. Don’t forget, though, that EBT is easier to read and reason about.
I mean, look at that:
"Multiples of 15"{forAll<Int>{i->if(Math.abs(i)>=Integer.MAX_VALUE/15-1)return@forAlltruevalnb=15*Math.abs(i)fizzbuzz(nb)=="FizzBuzz"}}
There is just so much stuff involved here. Show that to an apprentice-level developer and they will have a lot of questions about this test. It is not simple.
Another way to look at this “simplest possible way to make the failing test pass” is to imagine you have a developer on the team, who is removing any code that is not tested.
So that additional if statement is not tested by the test. That means it can be safely removed.
You can apply such radical technique to teams that do TDD rigorously. And if you knew there is a person on your team (and that person – is every member of the team), you are probably going to make sure to have all the production code covered.
You just don’t add any code that is not tested, as simple as that.
One of the most salient features of our Tech Hiring culture is that there is so much bullshit. Everyone knows this. Each of us contributes his share. But we tend to take the situation for granted.
For this "Multiples of 15" test, my refactored version would be something like
"Multiples of 15"{forAll(BigInteger.positive()){nb->fizzbuzz(15*nb)=="FizzBuzz"}}
As you can see one small pb was that I was too lazy to lookup how to test for only positive numbers. The harder and much more interesting one is I'm not used to think about integer overflows. This style of testing forced me to think about it because my test kept failing! That was actually a teachable lesson.
Anyway I highly recommend this, most fun presentation about testing I have ever read!
I can very easily write a fizzbuzz implementation that pass all your tests but is completly wrong. For example:
So I would throw in properties based testing, here with kotlin test
Yes. You can.
But will you, really?
IMHO alert.
In Test-Driven Development, you pass the failing test with the simplest possible solution. And your solution has a higher complexity than the one proposed in the article.
You have one edge case handler more (additional “if” statement).
Therefore, if TDD practiced properly, such “evil” solution will not be written.
Anyways.
I agree that PBT has a higher degree of correctness when it comes to testing. Don’t forget, though, that EBT is easier to read and reason about.
I mean, look at that:
There is just so much stuff involved here. Show that to an apprentice-level developer and they will have a lot of questions about this test. It is not simple.
Another way to look at this “simplest possible way to make the failing test pass” is to imagine you have a developer on the team, who is removing any code that is not tested.
So that additional if statement is not tested by the test. That means it can be safely removed.
You can apply such radical technique to teams that do TDD rigorously. And if you knew there is a person on your team (and that person – is every member of the team), you are probably going to make sure to have all the production code covered.
You just don’t add any code that is not tested, as simple as that.
For this "Multiples of 15" test, my refactored version would be something like
As you can see one small pb was that I was too lazy to lookup how to test for only positive numbers. The harder and much more interesting one is I'm not used to think about integer overflows. This style of testing forced me to think about it because my test kept failing! That was actually a teachable lesson.
Anyway I highly recommend this, most fun presentation about testing I have ever read!
slideshare.net/ScottWlaschin/an-in...
Great refactoring!
Still the "15 * nb" part requires a mental leap.
Example tests are nice because, when written well, they can be read very quickly (like prose).
Another thing. So what if I don't need my code to handle overflows? Because all the numbers that business wants to work with are under 500.
YAGNI in that case.
I've taken a look at the presentation. And I'm still convinced that 2 tests is enough to drive out correct "add" code if you follow 3 rules of TDD.
And if you doubt that I thought about this enough, here is my own presentation about PBT: github.com/waterlink/property-base...