Part 2: Brains before Beauty

Yvette
10 min readDec 28, 2015

--

This is the second part of the Getting Started with TDD in Swift tutorial.

We’re going to start build the logic of our app first. In order for our users to play FizzBuzz, our app needs to be able to check if a number is divisible by 3, 5, both, or neither. And because we’re doing TDD, we always start with a test

0. First thing, don’t panic if you see this warning about code signing. It’s intended to help you if you want to put your app on the App Store, and won’t get in the way of what we’re doing in this tutorial

Don’t panic!

1. If you look in the FizzBuzzTests folder, you’ll see that Xcode has given us some sample code to get us started, in the file called FizzBuzzTests.swift. Delete this file by clicking it, and hitting ⌘ + ⌫

On the pop-up that appears, click Move to Trash.

2. Add a new file into the FizzBuzzTests folder by selecting the folder and then pressing `⌘ + N`. Select a Unit Test Case Class, hit ‘Next’.

Select `Unit Test Case Class` and hit Next

Call the class BrainTests and make sure it’s a subclass of XCTestCase and the language is Swift. Hit ‘Next’ again, and save it within the FizzBuzzTests directory.

Make sure the target and group is set to FizzBuzzTests, and hit ‘Create’

As this project will only use Swift you don’t need to create a Objective-C bridging header. Select ‘Don’t Create’

No bridging header, thank you

3. Let’s have a look at what’s been created. We have a new class called BrainTests, which inherits from XCTestCase. There are four functions:

  • setUp
  • tearDown
  • testExample
  • testPerformanceExample

The setUp and tearDown are helper functions — they prepare the test and clean up afterwards, and are run every time for each test you write in this set. The testExample and testPerformanceExample are examples of different types of tests you can write. Delete them. It’s time to write our own!

4. Our first test will test that our app can tell if a number is divisible by three. It will look like this:

What is this code doing?

On this first line, we declare a new variable called brain. This is the instance of our soon-to-be-written Brain class, which will be where the domain logic for our app will sit.

The next line asks brain to check if the number 3 is divisible by 3, and assigns the result of this incredibly tricky maths to another variable called result.

Finally we use XCTAssertEqual to compare the result of our test to the expected result (in this case, true). We expect that when we ask our brain to check if 3 is divisible by 3, it will respond that it is true.

5. You may have noticed a slightly alarming red blob has appeared next to our code. This is Xcode’s way of alerting us to an error in our code that will prevent us compiling our app. You can get more information about the warning by clicking on it:

Chill, Xcode. We’ve got this.

It’s telling us that our app doesn’t know what a Brain is.

THIS IS FINE. Do not panic.

An important part of TDD is learning to love failing tests and warnings.

The aim is to let these warnings guide us to what code we need to write next.

6. The warning is telling us we need to define our Brain. Create a new file in the FizzBuzz folder. This time we want a Swift file called Brain.swift. Make sure when creating the file that the target is set to FizzBuzz.

In the new file write the following:

If you then go back to BrainTests.swift you might expect the warning to be gone — we’ve defined Brain. But it hasn’t!?

Why? Because there’s one more bit we need to do — we need to import our FizzBuzz project into our test file.

At the top of the BrainTests.swift, under the import XCTest line add:

@testable import FizzBuzz

If you see an error saying ‘No such module FizzBuzz` don’t worry. It’s just because we’ve never run our project. Hit the ‘Play’ button in the top left of the window, wait until you see ‘Build Succeeded’ pop up on the screen, then continue.

Press ⌘ + SHIFT + K to clean your project, and then first error should go away.

7. However, now we have a new error!

This error is telling us that although we now have a brain, it doesn’t know what isDivisibleByThree means. Let’s fix this.

(If the error isn’t immediately showing, hit ⌘ + U to run the tests, and the error should appear)

8. In the Brain.swift class, type:

Let’s have a quick look at what this code is doing.

The first line defines a new function called isDivisibleByThree. It takes one parameter called number, which is an integer, and the function returns a boolean (either true or false).

The second line does what the function promises — it returns a boolean (in this case, true).

9. Hop back over to your tests, and the warning should be gone (if it’s not, try hitting ⌘ + SHIFT + K. This cleans your project, and gives Xcode a kick to catch up with what you’ve written).

If you have an iPhone or iPad plugged into your computer Xcode will automatically try running the tests on your device. To avoid code signing issues, I’d recommend running the tests on one of the simulators that come with Xcode. Pick whichever you fancy.

So many different devices to pick from 😍

Now we don’t have any errors standing in our way, let run our test! (⌘ + U).

SUCCESS!

Next to the code for your first test, you should see a delightful green tick. High-five the nearest person you can find (it’s traditional)

Now, this may have felt a little like cheating. Our isDivisibleByThree method doesn’t actually do any calculating or logic. It just returns true! But this is another important part of TDD:

Only write the code needed to get your tests passing — nothing more.

If the current tests don’t cover all the functionality that you want from your function, class or app, the answer is simple: Write more tests!

10. In order to extend the functionality of our isDivisibleByThree method, we’ll test the opposite case: checking a number NOT divisible by 3.

CHALLENGE 1: Write a new test, that asks our brain if 1 is divisible by 3. Don’t worry about making it pass yet, just write the test.

Check your answer here.

Run your tests, and make sure it’s failing.

It’s important to run your tests, and see them failing. Otherwise when you write the code to get the test passing, you can’t be sure you’re not getting a false-positive.

11. We’ve now got a failing test — we’re expecting it to return false, but it’s returning true. So, we need to update our isDivisibleByThree method.

How do we know if a number is divisible by 3? If we divide it by 3, there should be no remainder. In code, we write it like this:

Once this code is in, run your tests again, and all being well, go find someone to high-five again.

12. The brain can deal with 3, but it also need to be able to check is a number is divisible by 5.

CHALLENGE 2: Write your tests for checking if a number is divisible by 5.

Answer here.

CHALLENGE 3: Make your tests for isDivisibleByFive pass.

Answer here.

13. At this point, you should have 4 passing tests, which is awesome! However, there are 3 stages in the TDD cycle:

  1. RED — Write a test, and watch it fail.
  2. GREEN — Write just enough code to make your test pass.
  3. REFACTOR — Clean up existing code.
  4. Repeat the cycle until you have an app!

We’ve done the first 2 stages several times, but haven’t yet cleaned up our code. Everyone will have their own idea of what ‘clean’ code looks like, and I’m not going to go into the subject here. But one good principle that most developers agree on is the DRY principle (aka Don’t Repeat Yourself).

In our test code we have an obvious candidate for refactoring: this line is repeated in our code 4 times already (and we have more tests to write).

let brain = Brain() 

So, lets clean it up.

We’ll make brain a class variable in our BrainTests class by pulling it out of each individual function, and declare it just once.

So, put the line just below the class declaration, and delete every other instance of it.

Before moving on, run your tests again to check the refactoring hasn’t broken anything.

14. Next, let’s implement the final check we need for our FizzBuzz rules — to check if a number is divisible by 3 AND 5. There’s a few different ways to do this, but one of the easiest is simply check if a number is divisible by 15.

CHALLENGE 4: The same as before, write tests for the Brain class to check whether a number is divisible by 15.

Answer here.

CHALLENGE 5: And now, make those tests pass.

Answer here.

High-fives all round!

15. Time for more refactoring, this time in our Brain class. The logic within each of our isDivisibleBy methods is basically the same, with only the dividing number changing. So, let’s move the logic into it’s own function:

In fact, we can refactor this function even further:

Now, we can replace the body of our other methods with this function. For example:

CHALLENGE 6: Refactor the isDivisbleByFive and isDivisibleByFifteen methods

Answer here.

16. The final bit of functionality this class needs is to return the right answer (Fizz, Buzz, or FizzBuzz) when given a number. We’ll use this later to compare the answer our user gives us, and check if their answer is right or not.

First let’s write a test to check that our app can ‘say’ Fizz, by returning it as a string.

CHALLENGE 7: Write the check method for the Brain, and remember, write as little code as needed to make the test pass!

Answer here.

17. Next, let’s see if we can make our brain say “Buzz”

CHALLENGE 8: Write a test to see if the check method returns “Buzz” when passed `5` as an argument.

Answer here.

Remember, run your test and check it fails before continuing. If you want to just run a single test, rather than the whole test suite (which is what ⌘+U does), you can click on the small cross or tick next to the test in Xcode.

Now time to make the new test pass.

CHALLENGE 9: Improve the check method, and make both tests pass. Hint: You’ll need an if/else statement!

Answer here.

18. Only two more tests to go!

CHALLENGE 10: Write a test to see if check will say “FizzBuzz” for 15

Answer here.

Getting this test is a little trickier to get passing. You might try:

However, if you run your test, you’ll see you get a failure

The error is telling us that the result of our test is returning “Fizz”, instead of the expected “FizzBuzz”. Because or the order of the if statements in our check function, isDivisibleByThree(15) is true, and so the function returns “Fizz”, never getting to the isDivisibleByFifteen line.

CHALLENGE 11: Fix the check method so that the tests all pass

Answer here.

Well done!

19. So close to finishing

CHALLENGE 12: Write the test case for passing check 1, which should return “1”.

Remember, because Swift has something called “type safety”, check must return a String, not an Integer.

Answer here.

CHALLENGE 13: Extend the check method, so that it returns the number in a string for 1.

Hint: You’ll need to use string interpolation in order to put your number into a String. In Swift you do this like this:

let string = “\(number)”

Answer here.

20. Well done! You should now have 10 passing tests, and a working, fully tested brain for your app.

If you want to test yourself, delete everything you just wrote and try and write it again! This is a called a kata — a small, repeatable coding challenge

Next: Part 3: The Game is On

If you found this tutorial helpful, please 💚 it below and follow me!

--

--

Yvette

iOS Developer | @makersacademy Alumni | Runner, Triathlete, ex-Rower | Feminist