More Videos

  • Testing Tips & Tricks

    Testing is an essential tool to consistently verify your code works correctly, but often your code has dependencies that are out of your control. Discover techniques for making hard-to-test code testable on Apple platforms using XCTest. Learn a variety of tips for writing higher-quality tests that run fast and require less maintenance.

    Resources

    Related Videos

    WWDC22

    WWDC20

  • Hello.Welcome to Testing Tips &Tricks.My name is Brian Croom.My colleague, Stuart, and I arereally excited to share somegreat testing techniques withyou that we have been learningrecently.As the conference wasapproaching, we thought it wouldbe really cool to have an appthat we could use to find coolthings to see and do around thearea of the convention center.We've been building this app,giving it views for findingvarious point of interest aroundSan Jose and listing how farthey are away from you.Now, of course, we wanted tomake sure we had a really greattest suite for this app that wecould run to give us confidencethat our code was workingproperly and would keep workingas we continue development.Today, we want to share foursimple techniques with you thatwe found really helpful whilewriting tests for our app.Some strategies for testingnetworking code in your app,some tips for tests dealing withfoundation notification objects,ways to take advantage ofprotocols when making mockobjects in your tests, and a fewtechniques for keeping yourtests running really fast.Let's start talking aboutnetworking.

    To allow for dynamic contentupdates, we've been building ourapp to load its data from aremote web server.

    Here are some things that wefound useful when writing testsfor networking code.But first, quick, a recap fromlast year.At WWDC 2017's EngineeringTestability session, wediscussed the pyramid model as aguide to how to structure a testsuite, balancing thoroughness,understandability, and executionspeed.

    In summary, an ideal test suitetends to be composed of a largepercentage of focused unittests, exercising individualclasses and methods in your app.

    These are characterized by beingsimple to read, producing clearfailure messages when we detecta problem, and by running veryquickly, often in the order ofhundreds or thousands of testsper minute.

    These are complemented by asmaller number of medium-sizedintegration tests that targeteddiscreet subsystem or cluster ofclasses in your app, checkingthat they worked together properly, each taking no morethan a few seconds to run.

    And the suite is topped off by ahandful of end-to-end systemtests, most often taking theform of UI tests that exercisethe app in a way very similar tohow the end-user will do so ontheir devices, checking that allthe pieces are hooked upproperly and interact well withthe underlying operating systemand external resources.A test suite following thismodel can provide acomprehensive picture of how anapp code's base is functioning.

    For testing the networking stackin this app, we really wanted totake the pyramid model to heartand use it as a guide for how tostructure our test suite.Here, we see the high-level dataflow involved in making anetwork request in the app andfeeding the data into the UI.

    In an early prototype of theapp, we had a method in our viewcontroller that was doing all ofthis in a single place, and itlooked like this.

    The method takes a parameterwith the user's location anduses that to construct a URL fora service API endpoint with alocation as query parameters.

    Then it uses Foundation'sURLSession APIs to make a datatask for a get request to thatURL.

    Then the server responds, itwould unwrap the data, decode itusing foundation's JSONDecoderAPI, into an array of point ofinterest values, which is astruct that I declared elsewhereand conforms the decodableprotocol. And it stores that into aproperty to drive a table viewimplementation,putting it onto the screen.

    Now, it's pretty remarkable thatI was able to do all of this injust about 15 lines of code,leveraging the power of Swiftand Foundation, but, by puttingthis together in the one method,I the maintainability and especiallythe testability of this code.Looking at the base of ourtesting pyramid, what we reallywant to be able to do here iswrite focus unit tests for eachof these pieces of the flow.Let's first consider the requestpreparation and response parsingsteps.

    In order to make this code moretestable, we started by pullingit out of the view controllerand made two methods on thisdedicatedPointsOfInterestRequest type,giving us two nicely decoupledmethods that each take somevalues as input and transformthem into some output valueswithout any side effects.

    This makes it verystraightforward for us to writea focused unit test for thecode. Here we're testing themakeRequest method just bymaking a sample and putlocation, passing it into themethod, and making someassertions about its returnvalue.

    Similarly, we can test theresponse parsing by passing insome mock JSON and makingassertions about the parsedresult.

    One other thing to note aboutthis test is that I'm takingadvantage of XCTest support fortest methods marked as throws,allowing me to use try in mytest code without needing anexplicit do catch block aroundit.

    Now, let's see the code forinteracting with URL session.

    Here, again, we pull it out theview controller, made aAPIRequest protocol with methodsmatching the signature of themethods from the request typethat we just saw.And this is used by anAPIRequestLoader class.

    That's initialized with arequest type and a urlSessioninstance.This class has a loadAPIRequestmethod which uses thatapiRequest value to generate aURL request.Feed that into the urlSession,and then use the apiRequestagain to parse in your response.Now, we can continue write unittest for this method, but rightnow I actually want to move upthe pyramid and look at amidlevel integration test thatcovers several pieces of thisdata flow.

    Another thing that I really wantto also be able to test at thislayer of my suite is that myinteraction with the URLSessionAPIs is correct.It turns out that the foundationURL loading system provides agreat hook for doing this.URLSession provides a high levelAPI for apps to use to performnetwork requests.

    objects likeURLSession data tests thatrepresent an inflight request.Behind the scenes though,there's another lower-level APIURLProtocol which performs theunderlying work of openingnetwork connection, writing therequest, and reading back aresponse.

    URLProtocol is designed to besubclassed giving anextensibility point for the URLloading system.

    Foundation provides built-inprotocols subclasses for commonprotocols like HTTPS.But we can override these in ourtests by providing a mockprotocol that lets us makeassertions about requests thatare coming out and provide mockresponses.

    URLProtocol communicatesprogress back to the system viathe URLProtocolClient protocol.We can use this in this way.

    We make a MockURLProtocol classin our test bundle, overridingcanInit request to indicate tothe system that we're interestedin any request that it offersus.

    Implement canonicalRequest forrequest, but the start loadingand stop loading method's wheremost of the action happens.

    To give our tests a way to hookinto this URLProtocol, we'llprovide a closure propertyrequestHandler for the test toset.

    When a URLSession task begins,the system will instantiate ourURLProtocol subclass, giving itthe URLRequest value and aURLProtocol client instance.

    Then it'll call our startLoadingmethod, where we'll take ourrequestHandler to the testsubsets and call it with aURLRequest as a parameter.

    We'll take what it returns andpass it back to the system,either as a URL response plusdata, or as an error.If you want to do test requestcancellation, we could dosomething similar in astopLoading methodimplementation.

    With the stub protocol in hand,we can write our test.We set up by making anAPIRequestLoader instance,configure it with a request typeand a URLSession that's beenconfigured to use ourURLProtocol.

    In the test body, we set arequestHandler on theMockURLProtocol, makingassertions about the requestthat's going out, then providinga stub response.Then we can call loadAPIRequest,waiting for the completion blockto be called, making assertionsabout the parsed response.

    Couple of tests at this layercan give us a lot of confidencethat our code is workingtogether well and, also, thatwe're integrating properly withthe system.For example, this test that wejust saw would have failed if Ihad forgotten to call a resumeon my data task.I'm sure I'm not the only onewho's made that mistake.

    Finally, it can also be reallyvaluable to include some systemlevel end-to-end tests.

    Actually test a UI testing canbe a great tool for this.To learn more about UI testing,refer to the UI testing in Xcodesession from WWDC 2015.Now, a significant challengethat you encounter when youstart to write true end-to-endtests is that when somethinggoes wrong when you get a test failure, it can be really hardto know where to start lookingfor the source of the problem.One thing that we were doing inour test recently to helpmitigate this was to set up alocal instance of a mock server,interrupting our UI tests tomake requests against thatinstead of the real server.

    This allowed our UI test to bemuch more reliable, because wehad control over the data beingfed back into the app.

    Now, while using a mock servercan be really useful in thiscontext, it is also good to havesome tests making requestsagainst the real server.One cool technique for doingthis can be to have some testsin the unit testing bundle thatcall directly into your apps inthat work in Stack and use thatto direct requests against thereal server.

    This provides a way of verifyingthat the server is acceptingrequests the way that your appis making them and that you'reable to parse the server'sresponses without having to dealwith the complications oftesting your UI at the sametime.

    So, to wrap up, we've seen anexample of breaking code intosmaller, independent pieces tofacilitate unit testing.We've seen how URLProtocol canbe used as a tool for mockingnetwork requests,and we've discussed how thepower of the pyramid can be usedto help us structure awell-balanced test suite that'llgive us good confidence in ourcode.Now, I want to call Stuart tothe stage to talk about somemore techniques.

    Thanks.

    Thanks, Brian.So, the first area I'd like totalk about is some bestpractices for testingnotifications.

    And to clarify, by notificationhere, I'm talking aboutfoundation-level notificationsknown as NSNotification andObjective-C.So, sometimes we need to testthat a subject observes anotification, while other timeswe need to test that a subjectposts a notification.Notifications are a one-to-manycommunication mechanism, andthat means that when a singlenotification is posted, it maybe sent to multiple recipientsthroughout your app or even inthe framework code running inyour app's process.So, because of this, it'simportant that we always testnotifications in an isolatedfashion to avoid unintended sideeffects, since that can lead toflaky, unreliable tests.So, let's look at an example ofsome code that has this problem.

    Here, I have thePointsOfInterest TableViewController from the appBrian and I are building.It shows a list of nearby placesof interest in a table view, andwhenever the app's locationauthorization changes, it mayneed to reload its data.So, it observes a notificationcalled authChanged from ourapp's CurrentLocationProviderclass.When it observes thisnotification, it reloads itsdata if necessary, and, then,for the purpose of this example,it sets a flag.This way, our test code cancheck that the flag to see ifthe notification was actuallyreceived.We can see here that it's usingthe default notification centerto add the observer.

    Let's take a look at what a unittest for this code might looklike.Here in our test for this class,we post the authChanged methodnotification to simulate it, andwe post it to the defaultNotificationCenter, the same onethat our view controller uses.Now, this test works, but it mayhave unknown side effectselsewhere in our app's code.It's common for some systemnotifications like UIapplicationsappDidFinishLaunchingnotification to be observed bymany layers and to have unknownside effects, or it could simplyslow down our tests.So, we'd like to isolate thiscode better to test this.There's a technique we can useto better isolate these tests.To use it, we first have torecognize thatNotificationCenter can havemultiple instances.As you may note, it has adefault instance as a classproperty, but it supportscreating additional instanceswhenever necessary, and this isgoing to be key to isolating ourtests.So, to apply this technique, wefirst have to create a newNotificationCenter, pass it toour subject and use it insteadof the default instance.This is often referred to asdependency injection.So, let's take a look at usingthis in our view controller.

    Here, I have the original codethat uses the defaultNotificationCenter, and I'llmodify it to use a separateinstance.I've added a newNotificationCenter property anda parameter in the initializerthat sets it.And, instead of adding anobserver to the default center,it uses this new property.I'll also add a defaultparameter value of .default tothe initializer, and this avoidsbreaking any existing code in myapp, since existing clientswon't need to pass the newparameter, only our unit testswill.

    Now let's go back and update ourtests.

    Here's the original test code,and now I've modified it to usea separate NotificationCenter.

    So, this shows how we can testthat our subject observes anotification, but how do we testthat our subject posts anotification?We'll use the same separateNotificationCenter trick again,but I'll also show how to makeuse of built-in expectation APIsto add a notification observer.

    So, here's another section ofcode from our app, theCurrentLocationProvider class.I'll talk more about this classlater, but notice that it hasthis method for signaling toother classes in my app that theapp's location authorization haschanged by posting anotification.

    As with our view controller,it's currently hardcoding thedefault NotificationCenter.And here's a unit test I wrotefor this class.It verifies that it posts anotification whenever theNotifyAuthChanged method iscalled, and we can see in themiddle section here that thistest uses the addObserver methodto create a block-basedobserver, and then it removesthat observer inside of theblock.

    Now, one improvement that I canmake to this test is to use thebuilt-inXCTNSNotificationExpectation APIto handle creating thisNotificationCenter observer forus.And this is a nice improvement,and it allows us to deleteseveral lines of code.

    But it still has the problem wesaw before of using the defaultNotificationCenter implicitly,so let's go fix that.Here's our original code, andI'll apply the same technique wesaw earlier of taking a separateNotificationCenter in ourinitializer, storing it, andusing it instead of the default.Going back to our test code now,I'll modify it to pass a newNotificationCenter to oursubject, but take a look at theexpectation now.

    When our tests are expecting toreceive a notification to aspecific center, we can pass theNotificationCenter parameter tothe initializer of the expectation.

    I'd also like to point out thatthe timeout of this expectationis 0, and that's because weactually expect it to alreadyhave been fulfilled by the timewe wait on it.That's because the notificationshould have already been postedby the time theNotifyAuthChanged methodreturns.So, using this pair oftechniques for testingnotifications we can ensure thatour tests remained fullyisolated, and we've made thechange without needing to modifyan existing code in our app,since we specified that defaultparameter value.So, next, I'd like to talk abouta frequent challenge whenwriting unit tests, interactingwith external classes.

    So, while developing your app,you've probably run intosituations where your class istalking to another class, eitherelsewhere in your app orprovided by the SDK.And you found it difficult towrite a test, because it's hardor even impossible to createthat external class.This happens a lot, especiallywith APIs that aren't designedto be created directly, and it'seven harder when those APIs havedelegate methods that you needto test.I'd like to show how we can useprotocols to solve this problemby mocking interaction withexternal classes but do sowithout making our tests lessreliable.

    In our app, we have aCurrentLocationProvider classthat uses CoreLocation.It creates a CLLocationManagerand configures it in itsinitializer, setting its desiredaccuracy property and settingitself as the delegate.Here's the meat of this class.It's a method calledcheckCurrentLocation.It requests the current locationand takes a completion blockthat returns whether thatlocation is a point of interest.So, notice that we're callingthe request location method onCLLocationManager, here.When we call this, it'll attemptto get the current location andeventually call a delegatemethod on our class.So, let's go look at thatdelegate method.We use an extension to conformto the CLLocationManagerDelegateprotocol here, and we call astored completion block.Okay, so let's try writing aunit test for this class.Here's one that I tried towrite, and, if we read throughit, we can see that it starts bycreating aCurrentLocationProvider andchecks that the desired accuracyand whether the delegate is set.So far, so good.But then things get tricky.We want to check thecheckCurrentLocation method,since that's where our mainlogic lives, but we have aproblem.We don't have a way to know whenthe request location method iscalled, since that's a method onCLLocationManager and not partour code.Another problem that we'relikely to encounter in this testis that CoreLocation requiresuser authorization, and thatshows a permission dialog on thedevice if it hasn't been grantedbefore.

    This causes our tests to rely ondevice state. It makes them harder to maintainand, ultimately, more likely tofail. So, if you've had this problemin the past, you may haveconsidered subclassing theexternal class and overridingany methods that you call on it.For example, we could trysubclassing CLLocationManagerhere and overriding theRequestLocation method.And that may work at first, butit's risky.Some classes from the SDK aren'tdesigned to be subclassed andmay behave differently.Plus, we still have to call thesuperclass' initializer, andthat's not code that we canoverride.But the main problem is that, ifI ever modify my code to callanother method onCLLocationManager, I'll have toremember to override that methodon my testing subclass as well.If I rely on subclassing, thecompiler won't notify me thatI've started calling anothermethod, and it's easy to forgetand break my tests.So, I don't recommend thismethod, and instead to mockexternal types using protocols.So, let's walk through how to dothat. Here's the original code, andthe first step is to define anew protocol.I've named my new protocolLocationFetcher, and it includesthe exact set of methods andproperties that my code usesfrom CLLocationManager.

    The member names and types matchexactly, and that allows me tocreate an empty extension onCLLocationManager that conformsto the protocol, since italready meets all therequirements.

    I'll then rename theLocationManager property toLocationFetcher, and I'll changeits type to the LocationFetcherprotocol.

    I'll also add a defaultparameter value to the initializer, just like I didearlier, to avoid breaking anyexisting app code.

    I need to make one small changeto the checkCurrentLocationmethod to use the renamedproperty.And, finally, let's look at thatdelegate method.This part is a little trickierto handle, because the delegateexpects the manager parameter tobe a real CLLocationManager, andnot my new protocol.So, things get a little morecomplicated when delegates areinvolved, but we can still applyprotocols here.Let's take a look at how.I'll go back to LocationFetcherprotocol that I defined earlier,and I'll rename that delegateproperty toLocationFetcherDelegate.And I'll change its type to anew protocol whose interface isnearly identical toCLLocationManagerDelegate, but Itweaked the method name, and Ichanged the type of the firstparameter to LocationFetcher.

    Now I need to implement theLocationFetcherDelegate propertyin my extension now, since it nolonger satisfies thatrequirement.And I'll implement the getterand the setter to use forcecasting to convert back andforth toCLLocationManagerDelegate, andI'll explain why I'm using forcecasting here in just a second.Then in my class' initializer, Ineed to replace the delegateproperty withlocationFetcherDelegate.And the last step is to changethe original extension toconform to the new mock delegateprotocol, and that part's easy--all I need to do is replace theprotocol and the methodsignature.

    But I actually still need toconform to the oldCLLocationManagerDelegateprotocol too, and that's becausethe real CLLocationManagerdoesn't know about my mockdelegate protocol.

    So, the trick here is to addback the extension whichconforms to the real delegateprotocol but have it call theequivalent locationFetcherdelegate method above.

    And earlier, I mentioned that Iused force casting in thedelegate getter and setter, andthat's to ensure that my classconforms to both of theseprotocols and that I haven'tforgotten one or the other.So, over in my unit tests, I'lldefine a struct nested in mytest class for mocking, whichconforms to the locationFetcherprotocol and fill out itsrequirements.

    Notice, in its RequestLocationmethod, it calls a block to geta fake location that I cancustomize in my tests, and thenit invokes the delegate method,passing it that fake location.Now that I have all the pieces Ineed, I can write my test.I create a MockLocationFetcherstruct and configure itshandleRequestLocation block toprovide a fake location.

    Then I create myCurrentLocationProvider, passingit the MockLocationFetcher.And, finally, I callcheckCurrentLocation with acompletion block.Inside the completion block,there's an assertion that checksthat the location actually is apoint of interest.So, it works. I can now mock my classes' usageof CLLocationManager withoutneeding to create a real one.So, here, I've shown how to useprotocols to mock interactionwith an external class and itsdelegate. Now, that was a lot of steps.So, let's recap what we did.First, we defined a newprotocol, representing theinterface of the external class.This protocol needs to includeall the methods and propertiesthat we use on the externalclass, and, often, theirdeclarations can match exactly.Next, we created an extension onthe original external class,which declares conformance tothe protocol.

    Then we replaced all our usageof the external class with ournew protocol, and we added aninitializer parameter so that wecould set this type in ourtests.We also talked about how to mocka delegate protocol, which is acommon pattern in the SDKs.

    There were a few more stepsinvolved here, but here's whatwe did.

    First, we defined a mockdelegate protocol with similarmethod signatures as theprotocol that we're mocking.But we replaced the real typewith our mock protocol type.Then, in our original mockprotocol, we renamed thedelegate property, and weimplemented that renamedproperty on our extension.

    So, although this approach mayrequire more code than analternative like subclassing,it'll be more reliable and lesslikely to break as I expand mycode over time, since this waythe compiler will enforce thatany new methods I call for mycode must be included in thesenew protocols.

    So, finally, I'd like to talkabout test execution speed.When your tests take a long timeto run, you're less likely torun them during development, oryou might be tempted to skip thelongest running ones.Our test suite helps us catchissues early, when fixingregression is easiest.So, we want to make sure ourtests always run as fast aspossible.Now, you might have encounteredtimes in the past when youneeded to artificially wait orsleep in your tests, because thecode your testing isasynchronous or uses a timer.

    Delayed actions can be tricky,so we want to be sure to includethem in our tests, but they canalso slow things down a lot ifwe're not careful.So, I'd like to talk about someways that we can avoid artificial delays in our tests,since they should never benecessary.Here's an example.In the points of interest appBrian and I are building, in themain UI, we have a strip at thebottom that shows the featuredplace.It basically loops through thetop places nearby, rotating toshow a new location every 10seconds.Now, there are several ways Imight implement this, but, here,I'm using the timer API forfoundation.Let's look at a unit test that Imight write for this class.It creates a FeaturedPlaceManager and storesits current place before callingthe scheduleNextPlace method.Then it runs the run loop for 11seconds. I added an extra second as agrace period.

    And, finally, it checks that thecurrentPlace changed at the end.Now, this isn't great, and ittakes a really long time to run.To mitigate this, we couldexpose a property in our code toallow us to customize thattimeout to something shorter,like 1 second.And here's what that kind of acode change might look like.

    Now, with this approach, we canreduce the delay in our testsdown to one second.Now, this solution is betterthan the one we had before.Our tests will definitely runfaster, but it still isn'tideal.Our code still has a delay, it'sjust shorter.And the real problem is that thecode we're testing is stilltiming dependent, which meansthat, as we make the expecteddelay shorter and shorter, ourtests may become less reliable,since they'll be more dependenton the CPU to schedule thingspredictably.And that's not always going tobe true, especially for asynchronous code.

    So, let's take a look at abetter approach.

    I recommend first identifyingthe delay mechanism.In my example, it was a timer,but you could also be using theasyncAfter API fromDispatchQueue.

    We want to mock this mechanismin our tests so that we caninvoke the delayed actionimmediately and bypass thedelay.

    Here's our original code again,and let's start by looking atwhat this scheduledTimer methodactually does.

    The scheduledTimer methodactually does two things for us.It creates a timer, and then itadds that timer to the currentrun loop.Now, this API can be reallyconvenient for creating a timer,but it would help us to makethis code more testable if Iactually break these two stepsapart.

    Here, I've transformed theprevious code from usingscheduledTimer to instead createthe timer first and then add itto the current runLoop second,which I have stored in a newproperty.Now, this code is equivalent towhat we had before, but, once webreak these two steps apart, wecan see that runLoop is justanother external class that thisclass interacts with.So, we can apply the mockingwith protocols technique wediscussed earlier here.

    To do that, we'll create a smallprotocol, containing thisaddTimer method.I've called this new protocolTimerScheduler, and it just hasthat one addTimer method, whichmatches the signature of therunLoop API.Now, back in my code, I need toreplace the runLoop with theprotocol that I just created.And in my tests, I don't want touse a real runLoop as myTimerScheduler.Instead, I want to create a mockscheduler that passes the timerto my tests.I'll do this by creating a newstruct nested in my unit testclass called MockTimerScheduler,conforming to the TimerSchedulerprotocol.

    It stores a block that will becalled whenever it's told to adda timer.And with all the pieces inplace, I can write my final unittest.First, I create aMockTimerScheduler and configureits handleAddTimer block.

    This block receives the timer.Once it's added to thescheduler, it records thetimer's delay, and then itinvokes the block by firing thetimer to bypass the delay.

    Then, we create aFeaturedPlaceManager and give itour MockTimerScheduler.

    And, finally, we callscheduleNextPlace to start thetest, and, voila, our tests nolonger have any delay.They execute super fast, andthey aren't timer dependent, soit'll be more reliable.

    And, as a bonus, I can nowverify the amount of timer delayusing this assertion at thebottom.And that's not something I wasable to do in the previous test.

    So, like I said, the delay inour code is fully eliminatedusing this technique.We think this was a great way totest code that involves delayedactions, but, for the fastestoverall execution speed in yourtests, it's still preferable tostructure the bulk of your teststo be direct and not need tomock delayed actions at all.For example, in our app, theaction being delayed waschanging to the next featuredplace.I probably only need one or twotests that show that the timerdelay works properly.And, for the rest of the class,I can call the show next placemethod directly and not need tomock a timer scheduler at all.While we're on the topic of textexecution speed, we had a coupleof other tips to share.

    One area we've seen concerns theuse of NSPredicateExpectations.We wanted to mention that theseare not nearly as performant asother expectation classes, sincethey rely on polar rather thanmore direct callback mechanisms.They're mainly used in UI tests,where the conditions beingevaluated are happening inanother process.So, in your unit tests, werecommend more directmechanisms, such as regularXCTestExpectations,NSNotification, orKVOExpectations.

    Another testing speed tip is toensure that your app launches asquickly as possible.Now, most apps have to do someamount of setup work at launchtime, and, although that work isnecessary for regular applaunches, when your app is beinglaunched as a test runner, a lotof that work may be unnecessary.Things like loading viewcontrollers, kicking off networkrequests, or configuringanalytics packages-- these areall examples of things that arecommonly unnecessary in unittesting scenarios.

    XCTest waits until your appdelegates did finish launchingmethod returns before beginningto run tests.So, if you profile and noticethat app launch is taking a longtime in your tests, then one tipis to detect when your app islaunched as a test runner andavoid this work.One way to do this is to specifya custom environment variable orlaunch argument.Open the scheme editor, go tothe test action on the leftside, then to the arguments tab,and add either an environmentvariable or a launch argument.In this screenshot, I've addedan environment variable namedIS-UNIT-TESTING set to 1.

    Then, modify your app delegate'sappDidFinishLaunching code tocheck for this condition, usingcode similar to this.

    Now, if you do this, be surethat the code you skip truly isnonessential for your unit testto function.

    So, to wrap up, Brian started byreminding us about the testingpyramid and how to have abalanced testing strategy inyour app, showing severalpractical techniques for testingnetwork operations.

    Then, I talked about isolatingfoundation notifications andusing dependency injection.

    We offered a solution to one ofthe most common challenges whenwriting tests, interacting withexternal classes, even if theyhave a delegate.

    And we shared some tips forkeeping your tests running fastand avoiding artificial delays.

    We really hope you'll find thesetests useful and look for waysto apply them the next timeyou're writing tests.

    For more information, check outour session webpage at thislink, and, in case you missedit, we hope you'll check outWednesday's What's New inTesting session on video.Thanks so much, and I hope youhad a great WWDC.[ Applause ]