Slide 1

Slide 1 text

Unit Testing in Swift or what I learned after getting it wrong a lot.

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

New rules with Xcode 7 and Swift If you get this error, Do this, Start using @testable

Slide 6

Slide 6 text

In Objective-C we had OCUnit and OCMock

Slide 7

Slide 7 text

Had to pull in all kinds of headers when testing a complex UIViewController

Slide 8

Slide 8 text

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.

Slide 9

Slide 9 text

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.

Slide 10

Slide 10 text

But how do you handle networking, Core Data, etc? Simple answer… you don’t or at least not directly in the ViewControllers

Slide 11

Slide 11 text

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.

Slide 12

Slide 12 text

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.

Slide 13

Slide 13 text

Lets create a quick viewController test suite

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

Lets instantiate the ViewController, then make our first failing test case.

Slide 16

Slide 16 text

As we expected, it failed, in fact it should always fail first if you’re doing Test-First

Slide 17

Slide 17 text

Still not passing… Now comes the dilemma, what do I do to force the view to load, layout, etc. ?

Slide 18

Slide 18 text

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/

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Next expectation was a navigation bar color… hmm… no navbar to be found, or Navigation Controller.

Slide 21

Slide 21 text

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()

Slide 22

Slide 22 text

remember we have a Navigation Controller starting off the workflow in the storyboard

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Now just update your setup code again to grab that appearance proxy usage Doh! Now it’s failing again…

Slide 25

Slide 25 text

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.

Slide 26

Slide 26 text

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.

Slide 27

Slide 27 text

The key to mocking and stubbing is to use a protocol

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

Add the protocol (not the MyService class) as a property to your ViewController

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Now verify with the mock (flagged) behavior with a test Now is a good time to use tearDown to reset the flag

Slide 32

Slide 32 text

That was a mock test, how about a stub? Yes, you can directly call the UITableViewDelegate and datasource methods in tests.

Slide 33

Slide 33 text

Now to make that test pass, code like you normally would if the service responded back with a set of strings.

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

No content