Notes on XCTest Framework

Don't comment-out a Test

Instead - Expect a Failure 


Here is how:  Unit Test Expected Failures in Swift

Put this code XCTExpectFailure("fix this later") right before your expected test failure.

The Xcode UI hides the disabled test from your view (a poor UI choice) and that is unfortunate... but you can do better.

Notes on XCTest Framework

In the Apple ecosystem I'm guessing your using XCode - so you have easy access to a Unit Testing Framework XCTest.  It's been around since version 5 of XCode ... that is a while back... maybe I'll look that up some time...  (2013).

Like all of Apple's documentation - it's very very sparse.  Here is the Overview:

"Use the XCTest framework to write unit tests for your Xcode projects that integrate seamlessly with Xcode's testing workflow.

Tests assert that certain conditions are satisfied during code execution, and record test failures (with optional messages) if those conditions aren’t satisfied. Tests can also measure the performance of blocks of code to check for performance regressions, and can interact with an application's UI to validate user interaction flows."

There is nothing earth shattering about that - unless you've been unit testing and coding for some time and then the word that should catch you attention is "performance" - WHAT!  XCTest is also a performance testbed... and it's very easy to use.

Apple Developer Documentation - XCTest

Notes on XCTest Usage:

Much like any xUnit framework ... one extends the XCTestCase with a new TestCase class and inside the class write multiple test methods (beginning with the word "test") and the framework will pick up this convention - it's a nice integration touch - thanks Apple!

The Apple class XCTAssert family of functions is used to check for Boolean conditions, nil or non-nil values, expected values, and thrown errors.

UI Test - or what I've always called Story Test are defined using User Interface Tests and the Performance Tests are also easy to use.


A great tutorial - with step by step guidance on setting up the XCTesting framework and XCode is found in Project 39 of Hacking with Swift.

Making a Unit Test Run Fast on validation of a View -> ViewInspector

This is an alternative to the Swift - XCode UI Test framework that has to run the App to drive the inspection and all the UI - which takes a lot of time (if your a computer). 

Let's try to install https://github.com/nalexn/ViewInspector via Swift Package Manager.  A nice testing utility for SwiftUI by Alexey Naumov (nalexn).  His blog article about View Inspector - Who said we cannot unit test SwiftUI Views?

In Xcode select File ⭢ Swift Packages ⭢ Add Package Dependency... Copy-paste repository URL: https://github.com/nalexn/ViewInspector Hit Next two times, under Add to Target select your test target. There is no need to add it to the build target. Hit Finish

Go into Project Build Setting - search for ENABLE_TESTING_SEARCH_PATHS and set to YES

Learn to use the Inspector:  https://github.com/nalexn/ViewInspector/blob/master/guide.md

There are 4 steps to configuring your XCTest Class file:

Look thru the Guide.md for examples - don't do what I did and skip ahead... go slow and test that the framework is installed and functioning properly - an important lesson to learn when practicing TDD.


Re-Learning to Practice TDD

I spent a few mornings practicing my TDD skills - a refresher course you might say - with James at Wingman Software.  Just watching - intently observing - James' technique was amazing.  Well worth it... only took a few hours to pay off when I couldn't get the newly installed ViewInspector testing for Swift UI Views to work.  I took a moment to try to practice what James had taught me.... SLOW d..o...w....n - one verfiable step at a time.
Solved my problem.  Thanks Wingman!

First Attempts to Practice TDD with ViewInspector

ViewInspector has some interesting notation required to traverse the view hiearchy ... RTFM!
let name = try ContentView().inspect().view( PlayerRowView.self ).hStack().text(2).string()

You could just power right in ... and fail - then toss out the tool or the technique that didn't give you 100% imediate gratification.  But - if you're reading this... you have the tanisity of a developer... so your not going to just toss it out.  Your going to try it properly.  Take satification even with accomplishing the example code in the Guide.

Now my first - nope - Nth impression is that writing a failing unit test for a View is very difficult.  For one I don't know SwiftUI View names very well and don't always know what is required to create the UI that I've vaguly have in mind.  Perhaps if someone handed me a wireframe design - or a sketch... maybe I should grab a whiteboard and make a sketch.  I don't want to spend more than 10 min. on this task - because I'm sure it will change and mutate toward the final product.

Navigation Bar - limits Find

In Jan 2021 we ran into this known issue trying to find elements of the menu in Baseball with View Inspector.  A Known Problem - being investigated.

Quote from the Guide:

Limitations

There are a few scenarious when find function is unable to automatically traverse the whole view.

One of such cases is a custom view that does not conform to Inspectable. Adding a corresponding extension in the test scope solves this problem.

In addition to that, there are a few SwiftUI modifiers which currently block the search:

While the first two can be unwrapped manually, the last two are notorious for blocking the inspection completely. The workaround is under investigation.


Another note in the View Inspector Guide

Views using @State, @Environment or @EnvironmentObject

Inspection of these views requires a tiny refactoring of the view's source code, and you can choose between two approaches: the first one is more lightweight, the second one is more flexible.

I decided to pass Game Objects to each of the Views in their constructors for my work around for a while.  I had started with passing Game in the Environment but ran into this snag with View Inspector.  I think it would be easy to switch out right before publishing.  But nice to have testing working smothly.


XCUI Recorder path Menu

I just noticed today that when recording events for XC UI Test some element queries have a menu disclosure button and one can select from various paths for those items.  It seems that Xcode defaults to a shortest path option.


Another trick is to print the view hierachy

let app = XCUIApplication()

print(app.debugDescription)

When having compile errors for x86_64 I found this advice to clean the cache of Library code...




=====

"Could not find or use auto-linked library 'XCTestSwiftSupport' ld: warning: Could not find or use auto-linked framework 'XCTest'"

In my case settings ENABLE_TESTING_SEARCH_PATHS to YES fixed the issue.

To really raise the bar  in  my  testing  ability  I  attended a TDD  course from  Wingman Software.   Watching and  trying  to  mirror James' step by step  process is  a truely  valuable experience.

XCODE CLOUD

I've set up my project to build in Apple's Cloud.  It was easy!

I did this largely because I spent a week banging my head on a compile issue that had so many red-hernings that it took a week to fix.  All that was wrong in the end... was that a view had a few problems... but Xcode didn't ever annotate or display the errors with the view.  It was having issues with the Indexing never finishing and the compile hanging and and and...
If I'd only had another build running so that I could get an independent second opinion of my source... I would have found the issues much quicker.  So I decided that Xcode Cloud Continous Integration would be a great resource.
Setting up the Build - maybe 10 min.

Now getting the Tests to run in the Cloud....  NOPE!  I've tried 3 or 15 times to configure the Workflow for Testing... it never has yet... not once... persist my setup.  Failing silently is not a good look - Apple!

Just to prove it works - I use the Duplicate and Save the copy!  Silent FAILURE.

Learning OS_LOG API

So it turns out learning to use the API is the easy part... collecting log files and READING them is the tough part...  I'm calling a friend...

I'm trying to remove all those pesky print statements - @LancerKind - have pity on a poor cave man.
See:  Learning iOS Logging