Test Driven Development Tutorial for iOS: Getting Started

In this Test Driven Development Tutorial, you will learn the basics of TDD and how to be effective at it as an iOS developer. By Christine Abernathy.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

Working on Test #3

You’ll skip testing 3 because it should pass based on the code you already wrote. You’ll also skip 4, at least for now, because it’s a special case that you’ll deal with later. So how about 5?

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForFive() {
  let result = converter.convert(5)
  XCTAssertEqual(result, "V", "Conversion for 5 is incorrect")
}

This tests the expected result for 5 which is V.

Run your new test. You’ll see a failure as five I’s isn’t the correct result:

In Converter.swift, replace convert(_:) with the following:

func convert(_ number: Int) -> String {
  if number == 5 {
    return "V"
  } else {
    return String(repeating: "I", count: number)
  }
}

You’re doing the minimum work here to get the tests to pass. The code checks 5 separately, otherwise it reverts to the previous implementation.

Run all your tests. They should pass:

Working on Test #4

Testing 6 presents another interesting challenge, as you’ll see in a moment.

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForSix() {
  let result = converter.convert(6)
  XCTAssertEqual(result, "VI", "Conversion for 6 is incorrect")
}

This tests the expected result for 6 which is VI.

Run your new test. You’ll see a failure since this is an unhandled scenario:

In Converter.swift, replace convert(_:) with the following:

func convert(_ number: Int) -> String {
  var result = "" // 1
  var localNumber = number // 2
  if localNumber >= 5 { // 3
    result += "V" // 4
    localNumber = localNumber - 5 // 5
  }
  result += String(repeating: "I", count: localNumber) // 6
  return result
}

The code does the following:

  1. Initializes an empty output string.
  2. Creates a local copy of the input to work with.
  3. Checks if the input is greater than or equal to 5.
  4. Appends the Roman numeral representation for 5 to the output.
  5. Decrements the local input by 5.
  6. Appends the output with a repeating count of the Roman numeral conversion for 1. The count is the previously decremented local input.

This seems like a reasonable algorithm to use based on what you’ve seen up to this point. It’s best to avoid the temptation of thinking too far ahead and handling other cases that you haven’t tested.

Run all of your tests. They should all pass:

Working on Test #5

You often have to be wise in picking what you test and when you test it. Testing 7 and 8 won’t yield anything new, and 9 is another special case, so you can skip it for now.

This brings you to 10 and should uncover some nuggets.

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForTen() {
  let result = converter.convert(10)
  XCTAssertEqual(result, "X", "Conversion for 10 is incorrect")
}

This tests the expected result for 10 which is a new symbol, X.

Run your new test. You’ll see a failure due to the unhandled scenario:

Switch to Converter.swift and add the following code to convert(_:) just after localNumber is declared:

if localNumber >= 10 { // 1
  result += "X" // 2
  localNumber = localNumber - 10 // 3
}

This is similar to how you previously handled 5. The code does the following:

  1. Checks if the input is 10 or greater.
  2. Appends the Roman numeral representation of 10 to the output result.
  3. Decrements 10 from a local copy of the input before passing execution to the next phases that handle 5 and 1’s.

Run all of your tests. They should all pass:

Uncovering a Pattern

As you build up your pattern, handling 20 seems like a good one to try out next.

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForTwenty() {
  let result = converter.convert(20)
  XCTAssertEqual(result, "XX", "Conversion for 20 is incorrect")
}

This tests the expected result for 20, which is the Roman numeral representation for 10 repeated twice, XX.

Run your new test. You’ll see a failure:

The actual result is XVIIIII, which doesn’t match what you expect.

Replace the conditional statement:

if localNumber >= 10 {

With the following:

while localNumber >= 10 {

This small change loops through the input when handling 10 instead of going through it just once. This appends a repeating X to the output based on the number of 10s.

Run all of your tests, and now they all pass:

Do you see a small pattern emerging? This is a good time to go back and handle the skipped special cases. You’ll start with 4.

Handling the Special Cases

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForFour() {
  let result = converter.convert(4)
  XCTAssertEqual(result, "IV", "Conversion for 4 is incorrect")
}

This tests the expected result for 4 which is IV. In Roman numeral land, 4 is represented as 5 minus 1.

Run your new test. You shouldn’t be too surprised to see a failure. It’s an unhandled scenario:

In Converter.swift, add the following to convert(_:) just before the statement that adds the repeating I:

if localNumber >= 4 {
  result += "IV"
  localNumber = localNumber - 4
}

This code checks if the local input after 10 and 5 have been handled is greater than or equal to 4. It then appends the Roman numeral representation for 4 before decrementing the local input by 4.

Run all of your tests. Once again, they’ll all pass:

You also skipped 9. It’s time to try it out.

In ConverterTests.swift, add the following new test to the end of the class:

func testConversionForNine() {
  let result = converter.convert(9)
  XCTAssertEqual(result, "IX", "Conversion for 9 is incorrect")
}

This tests the expected result for 9 which is IX.

Run your new test. The VIV result is incorrect:

Based on everything you’ve seen so far, do you have an idea about how you can fix this?

Switch to Converter.swift, and add the following to convert(_:), in between the code that handles 10 and 5:

if localNumber >= 9 {
  result += "IX"
  localNumber = localNumber - 9
}

This is similar to how you handled 4.

Run all of your tests, and again, they’ll all pass:

In case you missed it, here’s the pattern that emerged when handling many of the use cases:

  1. Check if your input is greater than or equal to a number.
  2. Build up the result by appending the Roman numeral representation for that number.
  3. Decrement your input by the number.
  4. Loop through and check the input again for certain numbers.

Keep this in the back of your mind as you move on to the next step in the TDD cycle.