Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit testings in Swift

Unit testings in Swift


Mark Wilkinson

March 30, 2016


  1. Unit Testing in Swift or what I learned after getting

    it wrong a lot.
  2. What we’ll cover What is a Unit Test and what

    is TDD Gotchas with Xcode and Testing What I used to do in obj-c and what I learned to do better in Swift real-world coding example of starting a Swift app with testing
  3. Unit testing is a development process in which the smallest

    testable parts of an application, called units, are individually and independently scrutinized for proper operation. Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test
  4. Unit Testing with Xcode has changed a little in each

    version. You could get away with more things like forcing views to load (to test your viewDidLoad logic) Objective-C was less restrictive in things being a dynamic language. Apple originally had no interest (it seemed) in the Unit Testing/TDD world, and did not get involved until iOS 7
  5. New rules with Xcode 7 and Swift If you get

    this error, Do this, Start using @testable
  6. In Objective-C we had OCUnit and OCMock

  7. Had to pull in all kinds of headers when testing

    a complex UIViewController
  8. In Swift now you just pull in the module of

    your app target Every test class you make now comes with a reminder to test performance.
  9. Most of your testing focus should be on ViewControllers in

    fact, your testing should start at the ViewController (one test class per each) and then refactoring will give you a model and service layer.
  10. But how do you handle networking, Core Data, etc? Simple

    answer… you don’t or at least not directly in the ViewControllers
  11. In most cases especially with networking, you’ll want to use

    a mock and/or stubbed replacement for the service. For CoreData you can test with a real stack but you need to clean the storage out after each run of the test suite, this is where the tearDown() method comes in handy.
  12. TDD and unit testing in general don’t work with tightly-coupled

    architectures. in fact there will probably be little you can test if separation of concerns and responsibilities are not assigned properly This is the silver-lining of unit testing, you enforce better coding practices the more you can test your app.
  13. Lets create a quick viewController test suite

  14. Layout your requirements first. Then form those into failing tests.

    1) Title in Navigation Bar (according to specs) should say “Magenic Mobile” 2) Navigation Bar color should be red
  15. Lets instantiate the ViewController, then make our first failing test

  16. As we expected, it failed, in fact it should always

    fail first if you’re doing Test-First
  17. Still not passing… Now comes the dilemma, what do I

    do to force the view to load, layout, etc. ?
  18. In Obj-C world you could just call loadView or directly

    call viewDidLoad Apple has specifically said now in documentation to not do either of those things in tests, instead just call the getter of the view. https://www.natashatherobot.com/ios-testing-view-controllers-swift/
  19. None
  20. Next expectation was a navigation bar color… hmm… no navbar

    to be found, or Navigation Controller.
  21. First rule of TDD!? write the failing test before attempting

    to solve the problem. To make this pass we’ll have to change how we created the viewController in setup()
  22. remember we have a Navigation Controller starting off the workflow

    in the storyboard
  23. Not the best way to set the bar background color

    especially (as in most cases) the color is needed for the whole app. Step 1, fail it Step 2, pass it Step 3, refactor it
  24. Now just update your setup code again to grab that

    appearance proxy usage Doh! Now it’s failing again…
  25. Now the hard part, how to deal with external dependencies

    There’s probably an infinite number of opinionated ways to do this. Make a mock, don’t use a mock, call a real service, etc. Mocks and Stubs have always worked for me in C# so I’ll stick with that.
  26. First off, we know we’ll have a network call to

    get a list of mobile group members. The key is to know what format is coming back, XML or JSON Then build a mock and stubbed service to emulate the behavior Once it’s fully emulated you can verify everything about the data, how to display it, how to parse it, etc.
  27. The key to mocking and stubbing is to use a

  28. In this simple contrived example we’re really just making a

    stub, a mock is really about verifying behavior. If you want to add some mock behavior, you can add flags for each method called. the flag is the mock behavior, the return of canned values is the stubbed behavior
  29. Add the protocol (not the MyService class) as a property

    to your ViewController
  30. Update your setup code to now inject this mock dependency

    It’s important that you do the dependency injection before you call the view, because
  31. Now verify with the mock (flagged) behavior with a test

    Now is a good time to use tearDown to reset the flag
  32. That was a mock test, how about a stub? Yes,

    you can directly call the UITableViewDelegate and datasource methods in tests.
  33. Now to make that test pass, code like you normally

    would if the service responded back with a set of strings.
  34. Hand-rolling your own mocks for the purpose of unit-testing is

    tedious and time consuming. Yes you can use some third-party mocking libraries, and if you only plan on using mocks for testing, I’d recommend that approach. But, I’d recommend doing the first approach with manually creating your mock classes that also stub out responses You get the added benefit of an offline service to test your app when the service is down
  35. None