A Live Developer Journal

TDD Fizzbuzz Kata in Kotlin

I'm currently working through Agile Technical Practices Distilled which has lots of practical tips and techniques that you can practice with the katas at the end of each chapter.

The first kata in the book, is Fizzbuzz, where you do nothing with non multiples of 3 and 5, get "Fizz" for multiples of 3, "Buzz" for multiples of 5 and "FizzBuzz" for multiples of 3 and 5.

I'm going to explain (document) my process for solving this problem while applying the techniques and principles learned in this chapter.

My Final Set of Tests and Code

I'm pretty happy with this attempt at solving the Fizzbuzz kata using an unfamiliar language, and not being confident in TDD or Object-oriented approaches.

I feel like I learned a lot. I got to practice 3 of the strategies metioned in the book (fake it, obvious implementation and triangulation). I learned from each of the error messages, which made it easier to predict and prevent future issues (especially compilation-based errors from not knowing the language well), and I learned how to remove duplication from tests (arrayOf and MapOf) etc.

I also think that this solution is much better than it would've been if I wasn't constantly aware of each tiny decision and being able to pause and ask myself what I both liked and disliked about my approach.

Yayy!

Test Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import org.junit.Assert.assertEquals
import org.junit.Test

class FizzbuzzShould {
    @Test
    fun notDivisibleBy3Or5() {
        arrayOf(1,2,4).forEach {
            assertEquals(it.toString(), Fizzbuzz(it).fizzBuzz())
        }
    }
    @Test
    fun divisibleBy3() {
        arrayOf(3,6,12).forEach {
            assertEquals("Fizz", Fizzbuzz(it).fizzBuzz())
        }
    }
    @Test
    fun divisibleBy5() {
        arrayOf(5,10,20).forEach {
            assertEquals("Buzz", Fizzbuzz(it).fizzBuzz())
        }
    }
    @Test
    fun divisibleBy3And5() {
        arrayOf(15,30,60).forEach {
            assertEquals("FizzBuzz", Fizzbuzz(it).fizzBuzz())
        }
    }
    @Test
    fun fizzBuzz() {
        mapOf(1 to "1",2 to "2",4 to "4", 6 to "Fizz", 12 to "Fizz", 15 to "FizzBuzz", 30 to "FizzBuzz", 60 to "FizzBuzz").entries.forEach {
            assertEquals(it.value, Fizzbuzz(it.key).fizzBuzz())
        }
    }
}

Production code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Fizzbuzz(private val num: Int = 0) {

    private fun divisibleBy3(): String {
        if (num % 3 == 0) { return "Fizz" }
        return ""
    }

    private fun divisibleBy5(): String {
        if(num % 5 == 0) { return "Buzz" }
        return ""
    }

    private fun divisibleBy3And5(): String {
        return divisibleBy3() + divisibleBy5()
    }

    private fun notDivisible(): String {
        return num.toString()
    }

    fun fizzBuzz(): String {
        var result = divisibleBy3And5()
        if(result != "") { return result }
        return notDivisible()
    }

}

Tech Choices

I'm using Kotlin to solve this problem. I don't know Kotlin very well, so I'm building out the katas with it to learn it along the way.

I'm using the Junit testing suite, which came with my IntelliJ IDE by default. I'm using Hamcrest to write my test assertions, because I feel like it lets me express my testing intent better.

I'm also using Git for version control, which I like to use with Gitmoji, which lets me put cute little emojis at the start of my commit messages. Each emoji has it's own tech theme to help me stay more consistent. My commit messages follows Chris Beam's Imperative Approach, where my commit message fills in the blank for the following sentence: "If applied, this commit message will ____________".

Fizzbuzz

The first thing I did was set up my test environment with a setup test to make sure my tests run and fail as expected. Here's what it looks like:

1
2
3
4
5
6
7
8
9
10
import org.hamcrest.core.IsEqual.equalTo
import org.junit.Assert.assertThat
import org.junit.Test

class FizzbuzzShould {
  @Test
  fun passSimpleSetupTest() {
    assertThat(true, equalTo(true))
  }
}

I prefer importing individual assertions that I'm actually using, instead of entire libraries.

My test follows the pattern outlined in the book. The class name follows the pattern "MyClassShould", while my test method name follows the pattern "doSomething". This way, when we run our tests, they will read like a list of actions your object knows how to do.

For my first test, I just wanted to make sure that the tests run and failed as expected. This first test has nothing to do with the actual problem.

Do nothing with non-multiples of 3 and 5

Now that I know the tests are up and running okay, it's time to write the first test.

This kata in the book has a few steps partially completed for us. The behaviour to test is given, but it's up to us to decide our own test names.

The first test for fizzbuzz in the book is described like this: "When I fizzbuzz number 1, I get back a string representing it".

The behaviour we are first testing is that we return the number when that number isn't a multiple of 3 or 5.

The challenge here is choosing a test name that doesn't reveal any implementation details. e.g. it doesn't say "return a string when...". Here's what I came up with:

1
2
3
4
@Test
fun doNothingWhenNotDivisibleBy3Or5() {
  assertEquals("1", fizzbuzz(1))
}

My test name is a bit long, but it's really clear what's going on. If I wanted to return the number as an int instead of a string, I can do that without having to change the test name.

Passing the test

When I ran the test, it failed, but not for the reason expected in the test assertion. It didn't fail because it expected "1" to be returned when a 1 is given to the fizzbuzz method.

It failed because of a compilation error "Unresolved reference: fizzbuzz".

Our first tiny step towards making the test pass is to create a fizzbuzz method.

1
fun fizzbuzz() {}

When we run the tests again, the same test fails, but the error message changed. This means that we passed the error message, and it's time to pass the next one, which tells us that the test assertion expected an argument but the method doesn't accept arguments. To make this error message pass, we need our method to expect an integer as an argument.

When we run the test this time, it fails because of the assertion instead of any compilation errors. The error message is: "Expected 1 but was kotlin.Unit".

All Kotlin methods have a default return type of Unit, which is the equivalent to "Void" in Java meaning that no meaningful value is returned.

We now have to write the production code that will make this test pass. There are three techniques outlined in the TDD chapter for doing this, they are:

I'll use the fake it strategy right now. I prefer doing this until my tests force me away from doing this, being lazy to force myself not to be lazy.

1
2
3
fun fizzbuzz(num: Int): String {
  return "1"
}

My test passes. I had to provide an explicit return type of "String" as well.

It's time to commit our code now that our test passes.

Still testing the same behaviour

We know that our fizzbuzz method does nothing when we give it a 1, but we also know that it might be given other numbers that are not divisible by 3 or 5 besides 1. So we have not yet finished testing this behaviour.

My next test is the same as the previous test, but given a different input value, this time a 2. The test fails because it expected "2" but got "1".

1
2
3
4
fun fizzbuzz(num: Int): String {
  if ( num == 1 ) { return "1" }
  return "2"
}

This time, I used the obvious implementation approach. If the only inputs this method was ever going to get was 1 or 2, this code would be sufficient. If not, then we need a new test to force it to be more generic (triangulation).

Time to commit again now that the test passes again.

I'm still not confident that the behaviour is fully tested, so I'll write another test, this time expecting the number 4. I skipped the number 3 because that is a multiple of 3 and would introduce new behaviour.

To make this test pass, I could use the obvious implementation strategy again. However, I know that if I do, that will be the 3rd instance of duplication (3 ifs). So instead, I'm going to remove the duplication by returning the input (converted to a string) directly.

1
2
3
4
fun fizzbuzz(num: Int): String {
  if ( num == 1 ) { return "1" }
  return num.toString()
}

Behaviour One Tests and Code

Tests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.hamcrest.core.IsEqual.equalTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Test
import org.junit.Ignore

class FizzbuzzShould {
    @Ignore @Test
    fun pass_simple_setup_test() {
        assertThat(true, equalTo(true))
    }
    @Test
    fun doNothingWhenNotDivisibleBy3Or5() {
        assertEquals("1", fizzbuzz(1))
        assertEquals("2", fizzbuzz(2))
        assertEquals("4", fizzbuzz(4))
    }
}

Code

1
2
3
fun fizzbuzz(num: Int): String {
    return num.toString()
}

Behaviour 2: fizz when number is divisible by 3

Now that I'm confident that the behaviour "do nothing when number is not divisible by 3 or 5", I'm happy to move onto testing the next behaviour "fizz when divisible by 3".

My new test looks like this:

1
2
3
4
@Test
fun fizzWhenDivisibleBy3() {
  assertEquals("Fizz", fizzbuzz(3))
}

To pass this test, I used the obvious implementation strategy again, because my tests currently don't force me to do otherwise.

1
2
3
4
fun fizzbuzz(num: Int): String {
    if (num == 3) { return "Fizz" }
    return num.toString()
}

The next step is to write another test to force me to be a little more generic:

1
assertEquals("Fizz", fizzbuzz(6))

I used obvious implementation again to pass this test:

1
2
3
4
fun fizzbuzz(num: Int): String {
    if (num == 3 || num == 6) { return "Fizz" }
    return num.toString()
}

I added another test to force the code to be even more generic:

1
assertEquals("Fizz", fizzbuzz(12))

As this would be the third instance of the if statement, I'll remove duplication and return "Fizz" if the number is divisible by 3 using the modulo operation.

1
2
3
4
fun fizzbuzz(num: Int): String {
    if (num % 3 == 0) { return "Fizz" }
    return num.toString()
}

Time to commit the code.

Pivot Point

The if statement in our production code represents a pivot point, where the code executes in two different behavioural directions. Either it returns the number if it isn't divisible by 3 or 5, or it returns "Fizz" if the number is divisible by 3.

It's good practice for a method to only do one thing at a time, so I think it's a good idea to split my fizzbuzz method into two individual methods that each handle a single responsibility.

I'll also rename the methods so that they more accurately reflect the behaviour they are responsible for.

Before:

1
2
3
4
fun fizzbuzz(num: Int): String {
    if (num % 3 == 0) { return "Fizz" }
    return num.toString()
}

After:

1
2
3
4
5
6
7
8
9
10
11
12
fun fizzbuzz(num: Int): String {
    return divisibleBy3(num)
}

fun divisibleBy3(num: Int): String {
    if (num % 3 == 0) { return "Fizz" }
    return notDivisible(num)
}

fun notDivisible(num: Int): String {
    return num.toString()
}

I don't know if this is the best way to handle things at the moment. I see a few problems with it, but it does split the behaviour into their own methods, which is better than it was before.

What is happening here is that when we call fizzbuzz, we check to see if our number is divisible by 3 by passing it over to the "divisibleBy3" method. If it isn't divisible by 3, then we call the "notDivisible" method which returns the number as a string.

Now that we have a couple examples of behaviour, I think we should add our methods to a class called "FizzBuzz", like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Fizzbuzz(private val num: Int = 0) {

    fun fizzbuzz(): String {
       return divisibleBy3()
    }

    private fun divisibleBy3(): String {
        if (num % 3 == 0) { return "Fizz" }
        return notDivisible()
    }

    private fun notDivisible(): String {
        return num.toString()
    }

}

I created a class called "Fizzbuzz", with an instance variable set to 0 by default, or any number that is passed in.

I kept all of the methods the same, but made them private.

After doing this, I needed to update the tests so that they called the method "fizzbuzz" through the class, rather than directly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FizzbuzzShould {
    @Ignore @Test
    fun pass_simple_setup_test() {
        assertThat(true, equalTo(true))
    }
    @Test
    fun doNothingWhenNotDivisibleBy3Or5() {
        assertEquals("1", Fizzbuzz(1).fizzbuzz())
        assertEquals("2", Fizzbuzz(2).fizzbuzz())
        assertEquals("4", Fizzbuzz(4).fizzbuzz())

    }
    @Test
    fun fizzWhenDivisibleBy3() {
        assertEquals("Fizz", Fizzbuzz(3).fizzbuzz())
        assertEquals("Fizz", Fizzbuzz(6).fizzbuzz())
        assertEquals("Fizz", Fizzbuzz(12).fizzbuzz())
    }
}

There's quite a lot of duplication here. It would be cool to get rid of some of that. Also, I'm going to rename the "fizzbuzz" method to "result" so it reads better in the tests.

I don't know how to remove the duplication right now so will leave it.

I also changed my mind about not calling "divisibleBy3" and "notDivisible" directly, because we're testing those specific behaviours.

I feel all over the place right now. Here's the new tests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FizzbuzzShould {
    @Ignore @Test
    fun pass_simple_setup_test() {
        assertThat(true, equalTo(true))
    }
    @Test
    fun notDivisibleBy3() {
        assertEquals("1", Fizzbuzz(1).notDivisible())
        assertEquals("2", Fizzbuzz(2).notDivisible())
        assertEquals("4", Fizzbuzz(4).notDivisible())

    }
    @Test
    fun divisibleBy3() {
        assertEquals("Fizz", Fizzbuzz(3).divisibleBy3())
        assertEquals("Fizz", Fizzbuzz(6).divisibleBy3())
        assertEquals("Fizz", Fizzbuzz(12).divisibleBy3())
    }
}

Divisible by 5

The next behaviour is to test whether the number is divisible by 5, and returning "buzz" when it is:

1
2
3
4
@Test
fun divisibleBy5() {
  assertEquals("Fizz", Fizzbuzz(5).divisibleBy5())
}

Passing the test:

1
2
3
4
fun divisibleBy5(): String {
  if(num % 5 == 0) { return "Buzz" }
  return notDivisible()
}

Divisible by 3 AND 5

The next behaviour is to return "FizzBuzz" if a number is divisible by both 3 and 5.

1
assertEquals("FizzBuzz", Fizzbuzz(15).divisibleBy3And5())

Code which passes the test:

1
2
3
4
fun divisibleBy3And5(): String {
    if( "${divisibleBy3()}${divisibleBy5()}" == "FizzBuzz") { return "FizzBuzz" }
    return notDivisible()
}

Tying it all together

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import org.hamcrest.core.IsEqual.equalTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThat
import org.junit.Test
import org.junit.Ignore

class FizzbuzzShould {
    @Ignore @Test
    fun pass_simple_setup_test() {
        assertThat(true, equalTo(true))
    }
    @Test
    fun notDivisibleBy3Or5() {
        assertEquals("1", Fizzbuzz(1).notDivisible())
        assertEquals("2", Fizzbuzz(2).notDivisible())
        assertEquals("4", Fizzbuzz(4).notDivisible())

    }
    @Test
    fun divisibleBy3() {
        assertEquals("Fizz", Fizzbuzz(3).divisibleBy3())
        assertEquals("Fizz", Fizzbuzz(6).divisibleBy3())
        assertEquals("Fizz", Fizzbuzz(12).divisibleBy3())
    }
    @Test
    fun divisibleBy5() {
        assertEquals("Buzz", Fizzbuzz(5).divisibleBy5())
        assertEquals("Buzz", Fizzbuzz(10).divisibleBy5())
        assertEquals("Buzz", Fizzbuzz(20).divisibleBy5())
    }
    @Test
    fun divisibleBy3And5() {
        assertEquals("FizzBuzz", Fizzbuzz(15).divisibleBy3And5())
        assertEquals("FizzBuzz", Fizzbuzz(30).divisibleBy3And5())
        assertEquals("FizzBuzz", Fizzbuzz(60).divisibleBy3And5())
    }
    @Test
    fun fizzBuzz() {
        assertEquals("1", Fizzbuzz(1).fizzBuzz())
        assertEquals("Fizz", Fizzbuzz(3).fizzBuzz())
        assertEquals("FizzBuzz", Fizzbuzz(15).fizzBuzz())
    }
}

Production code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Fizzbuzz(private val num: Int = 0) {

    fun divisibleBy3(): String {
        if (num % 3 == 0) { return "Fizz" }
        return divisibleBy5()
    }

    fun divisibleBy5(): String {
        if(num % 5 == 0) { return "Buzz" }
        return notDivisible()
    }

    fun divisibleBy3And5(): String {
        if( "${divisibleBy3()}${divisibleBy5()}" == "FizzBuzz") { return "FizzBuzz" }
        return divisibleBy3()
    }

    fun notDivisible(): String {
        return num.toString()
    }

    fun fizzBuzz(): String {
        return divisibleBy3And5()
    }

}

Here is what I like and dislike about my object-oriented, test-driven fizzbuzz attempt written in Kotlin

Dislikes

Likes

Final refactorings

Some of the dislike points above bothered me too much to leave alone, so I asked for help, woo woo!

The first major niggle for me was the test duplication. There were two techniques I was taught for removing it.

arrayOf

If you have multiple test cases that where the expected value stays the same but the input value changes, you can use arrayOf to loop through them, like this:

1
2
3
arrayOf(5,10,20).forEach {
  assertEquals("Buzz", Fizzbuzz(it).fizzBuzz())
}

mapOf

If you have multiple test cases where the expected value differs depending on which input value is given, mapOf is a good solution for storing the input as the key and the expected result as the value, like this:

1
2
3
mapOf(1 to "1", 6 to "Fizz", 15 to "FizzBuzz").entries.forEach {
  assertEquals(it.value, Fizzbuzz(it.key).fizzBuzz())
}

Preventing long method chains

The next major niggle was the method chains, where they all linked together. So I asked for help again and we ended up building up a string which ends up an empty string if a number is not divisible by 3 or 5, "Fizz" if the number is divisible by 3, and "Buzz" if the number is divisible by 5. The fizzbuzz method starts with the divisible by 3 method so if a number is divisible by 3 and 5, "Fizz" is added first, and "Buzz" is added second.

This was so much fun! Learned a lot!

The final test suite and production code for this problem is shown at the top of this article. I'm not entirely happy with my solution, but I'm happy with the progress made by attempting to implement it.

I'm going to have a look at the solution in the back of the book so that I can compare it with my solution and go from there. Happy dance!