The Wrong Way to Reduce Duplicate Test Code

Now we’re reaching the critical goal of this chapter: avoiding the testing mistake most Swift programmers make. Let’s apply a common Swift programmer approach to duplication. We’ll see how it creates problems for test code. Delete the XCTFail assertion in test_methodOne. This brings the MyClassTests suite back to this:

LifeCycle/LifeCycleTests/MyClassTests.swift
 class​ ​MyClassTests​: ​XCTestCase​ {
 
 func​ ​test_methodOne​() {
 let​ sut = ​MyClass​()
 
  sut.​methodOne​()
 
 // Normally, assert something
  }
 
 func​ ​test_methodTwo​() {
 let​ sut = ​MyClass​()
 
  sut.​methodTwo​()
 
 // Normally, assert something
  }
 
 }

See how each test starts with the same line, creating an instance of MyClass? A typical Swift programmer would think, “Why not promote sut from local variables to a property?” It would look like this:

LifeCycle/LifeCycleTests/MyClassTests.swift
 class​ ​MyClassTests​: ​XCTestCase​ {
 private​ ​let​ sut = ​MyClass​()
 
 func​ ​test_methodOne​() {
  sut.​methodOne​()
 
 // Normally, assert something
  }
 
 func​ ​test_methodTwo​() {
  sut.​methodTwo​()
 
 // Normally, assert something
  }
 }

Everything looks cleaner, and more “Swifty.” So what’s the problem? Run the tests and look at the test transcript. If you drill down to the “All tests” level, you’ll see something like this:

 >> MyClass.init() #1
 >> MyClass.init() #2
 Test Suite 'All tests' started at 2019-06-22 20:12:15.128
 Test Suite 'LifeCycleTests.xctest' started at 2019-06-22 20:12:15.128
 Test Suite 'MyClassTests' started at 2019-06-22 20:12:15.129
 Test Case '-[LifeCycleTests.MyClassTests test_methodOne]' started.
 >> methodOne
 Test Case '-[LifeCycleTests.MyClassTests test_methodOne]' passed
  (0.001 seconds).
 Test Case '-[LifeCycleTests.MyClassTests test_methodTwo]' started.
 >> methodTwo
 Test Case '-[LifeCycleTests.MyClassTests test_methodTwo]' passed
  (0.000 seconds).
 Test Suite 'MyClassTests' passed at 2019-06-22 20:12:15.131.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.001 (0.002)
  seconds
 Test Suite 'LifeCycleTests.xctest' passed at 2019-06-22 20:12:15.131.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.001 (0.003)
  seconds
 Test Suite 'All tests' passed at 2019-06-22 20:12:15.131.
  Executed 2 tests, with 0 failures (0 unexpected) in 0.001 (0.004)
  seconds

Look for the logging of the init and deinit calls.

Two instances of MyClass are created before tests are even run. And they’re never destroyed.

This is no longer the clean room we want for our tests. Problems can potentially multiply:

What is going on? Keep reading.

images/aside-icons/info.png

The problems that come with “legacy” code don’t apply to only production code. You can have “legacy” test code as well. Like any code, test code requires ongoing care to keep it in shape. Any tests that fail to preserve the clean room create potential problems. These problems can go unnoticed for a long time. So make sure to review the quality of test code, old and new, with the same rigor you apply to production code.