Shared unit tests for new multiplatform template in Xcode 12 - xcode

How do you test the shared portion of code when you have a multiplatform project? I see tests for iOS and tests for macOS, but nothing for the shared pieces. I want to add unit tests for the platform-independent portion of my app.

Required
The most important part is to import the module to the test target:
The #testable word means that this test can access the internal stuff of the imported module.
Also, note that the MultiplatformAPP is the name of my project. You should look for the name of your project, instead.
✅ Now you can access to the shared section and test it as you like. But there are some extra optional works you can do:
Optionally
You can build a custom bundle for your shared test:
Note that is doesn't matter what template group you are choosing this template from. It's a template! you can change it to anything else anytime. So we started with tvOS 🤷🏻‍♂️
Then you can nake it independent to any of the targets or depends on any of them you prefer to test with:
Note that I have named it Tests shared to match the pattern of the default tests naming convention of the multiplatform app.

Think we managed to figure this out.
If you go to File > New > Target ...
In the Multiplatform section there isn't many options, but in iOS there is a Testing bundle named "Unit Testing Bundle"
If you add that and name it "Tests Shared"
You can then unit test against files that are part of the Shared target

Related

Can an App Extension be conditionally included in a build?

I'm starting work on 2 app extensions, and I'd like to set up my builds as follows:
Builds intended for the App Store should not include either extension
Builds intended for TestFlight should include the extension that is almost ready
Local builds should include both extensions
Currently, I have a different build configuration for each of these to show and hide other aspects of the app (and compiler flags in some cases too). I use fastlane to make my builds.
As far as I can tell, the only ways to add or remove an app extension would be:
Duplicate the main target twice and use one for each of the above builds (drawback - must maintain 3 copies of the target until the extensions are fully complete)
Manually add/remove the extension before building (drawback - impossible to make both TestFlight and App Store builds from a given branch in a CI environment because manual intervention is required)
Can anyone think of a better solution for conditionally including the app extensions?

Can I link an XCUITest target to a different Project's app target OR use XCTest/XCUITest outside of a UI Test Bundle target?

I am currently building out a test suite in an Xcode workspace that contains 3 existing projects. 2 App projects and a common framework project. Both apps are very similar in regards to UI with some minor differences.
I am trying to find a way to share the XCUITest framework I made for App A with App B.
Currently, App A has a test target set inside of its AppA.xcodeproj. All of App A's tests are doing great and easy to maintain. I need to extend this to App B and refactor App A's framework to only the App A exclusive bits so App A and App B can share the common XCUITest framework code base from a central base.
I have tried to create a new project, move over the UI test code and point the new UI Test target at App A's executable, but I could not figure out how to get the option to show.
I set the App A app as a Target Dependency in the New Project's test target > Build Phases tab, but no luck. I was able to set App A's target in the new test target's scheme but still landed on an error.
Assertion Failure: CommonBasePage.swift:21: failed: caught "NSInternalInconsistencyException",
"No target application path specified via test configuration: <XCTestConfiguration: 0x60400014e910>
testBundleURL:file:///Users/user.name/Library/Developer/Xcode/DerivedData/.../Debug-iphonesimulator/NewTestTarget-Runner.app/PlugIns/NewTestTarget.xctest/
testBundleRelativePath:(null)
productModuleName:NewTestTarget
testsToSkip:(null)
testsToRun:NewBasePage/testExample
reportResultsToIDE:YES
sessionIdentifier:<>
pathToXcodeReportingSocket:(null)
disablePerformanceMetrics:no
treatMissingBaselinesAsFailures:no
baselineFileURL:(null)
baselineFileRelativePath:(null)
targetApplicationPath:(null)
targetApplicationBundleID:(null)
testApplicationDependencies:
{...
I'm assuming targetApplicationPath:(null), targetApplicationBundleID:(null) should have legit target values but I'm unsure where else to look in order to set them.
I gave up on that and tried to create a new project & target as a Cocoa Touch Framework to shift the common code to a central point but was not able to import XCTest, which my framework depends on for XCUIApplication and XCUIElement fields.
import XCTest //Causes "Cannot load underlying module for 'XCTest'"I tried adding XCTest.framework to the targets "Link Binary with Libraries" section under build phases but no luck.
Is there a way to work with the XCTest framework outside of a test target?
OR
Is there a way to point one project's test target at another project's app target?
You can link XCTest and any other framework to anything. Make sure you have $(PLATFORM_DIR)/Developer/Library/Frameworks in both LD_RUNPATH_SEARCH_PATHS and FRAMEWORK_SEARCH_PATHS.
Making framework for shared code is a good idea.

Having separate schemes for test targets in Xcode?

We have the following project structure:
Workspace:
- app project
- cocoapods project
App scheme:
- app target (run)
- ui test target (test)
- unit test target (test)
After upgrading to Xcode 10, Xcode insists on building the entire project, including pods and ui tests, every time I run a single unit test, which is a quite heavy and slow process.
If i create a seperate scheme which only include the unit and/or ui tests, it only rebuilds the tests when I run them. Just as I want.
However, as they are no longer member of the main app scheme, I can no longer press Test on the main scheme, as it no longer contains test targets.
My question is then: Is it normal or recommended to have seperate schemes for tests, or can I prevent the entire project from being rebuild in another way, when running a single unit test?
(note: I have set the Host Application setting to None on the unit tests, so I don't get why it always builds the entire project anyway?)
Why not both? In your "Test All the Things" scheme, add each test target. This scheme should be shared.
But when I'm working in one target, I make a scheme for it alone. (More accurately, AppCode creates one for me.) Such schemes are not shared.

How can I change the name of a release target without affecting the build of unit test target?

I have a release target and a test target, I want to change the name of the release target, but if I do so I start getting a linking error in the test target. As they are unit tests I didn't think there would/should be a dependency within the test target to the release target, but apparently there is.
The linkage error is:
ld: file not found: /Users/Cequint/Library/Developer/Xcode/DerivedData/XYZ-apppviisyaexwpdcwgjtfldjotro/Build/Products/Debug-iphonesimulator/XYZ.app/XYZ
Command /Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin/clang failed with exit code 1
XYZ.app is the name of the release target before I renamed it.
Is there an automatic or quick way of updating the unit test target so that it keeps in step?
Depending on how the unit test target was added, you may or may not have a build dependency in the test target to the app target. I would check there first in targets->test target->build phases->target dependencies. I would think XCode would automatically update that but you never know...
One more place to look for targets is in the unit test targets build scheme, where you should see the test target (plus optionally the project target itself, as I have a dependency on it in my personal test project).
Another place to test the reset method mentioned by others above is to go to the organizer, select projects, then 'delete...' all the derived data for the project in question.
Probably a bit late with this answer, but I experienced this same issue (Xcode 6.3) and figured out a fix:
Go into the Project Inspector and select the unit test target Locate
the Test Host value (it will be something like
$(BUILT_PRODUCTS_DIR)/MyOldAppTargetName.app/MyOldAppTargetName)
Edit the Test Host value to reflect the new app target name
(something like
$(BUILT_PRODUCTS_DIR)/MyNewAppTargetName.app/MyNewAppTargetName
I have filed this as http://www.openradar.me/21139630 and rdar://21139630 with Apple. Feel free to dupe if you also experience this issue.

In an Xcode 4 workspace, how do I cascade build settings & configs to subprojects

Overview
I'm using static libraries and Xcode 4 workspaces to effect modularity in iOS development, an increasingly common technique. For example, I might have a workspace which contains an App project, and a Library project, like so1:
You would then have a scheme to build these that looked something like this:
What I would like to do is have the "App build" control the "Library build" it initiates, in at least a couple of ways:
Map App configurations (e.g. Debug, AdHoc) to arbitrary Library configurations
Passing through some subset of -D defines, and/or specifying these for the library build.
I'll deal with each of these in their own section, but it's worth making a few clarifications.
Clarifications
I'm using App/Library here as an easy proxy for any Superproject/Subproject relationship you may have.
From what I've seen, Xcode 3 style embedded subprojects don't seem to work any differently in Xcode 4 than workspace "peers". I'd love to be wrong about this.
I know I could do almost anything with a "Run Build Script" build phase, and xcodebuild. But I'm trying to work within the system here, where the dependencies are specified in the scheme, and otherwise somewhat loosely coupled.
The Library exists to be used in more than just this project, and so you cannot arbitrarily load it up with junk specific to this App's build, or reference anything particular to the App or Workspace. For the general case, this rules out including static .xcconfig from the App project as a way to convey build information from the App to the Library.
Building the Library outside the workspace sacrifices too much, not an option.
Configuration Mapping
As I understand it, building a particular App configuration will:
If a configuration exists in the Library of the same name, it will build the Library using that.
Otherwise, it will build the active configuration of the Library, as specified in the Library's project file.
To my knowledge, without resorting to the aforementioned run-build-script hack, that is the extent of the control one has over subproject build configurations. Please tell me different.
Ideally, I would be able to specify (in the scheme, presumably):
AppConfigA -> LibConfig1
AppConfigB -> LibConfig2
While Debug, AdHoc, & Release may be the only configurations some ever use, complex projects often outgrow that.
Defines
I've not yet found way to pass -D defines from the App build to the Library, without resorting to xcodebuild, which can take, e.g., an .xcconfig file.
The App's build settings can be accessed in Library build run-build-script phase. However, doing that introduces a dependency in the Library on the App project, which for good reason is verboten (cf. Clarifications). But even then, I haven't found a way to use those settings to directly control the Library's build (much2).
So crazy it just might...
One scheme I came up with while writing this would be:
The Library bases it's build configurations on an empty (dummy) LibraryExternals.xcconfig file within it's own project.
A clean of Library deletes that file. A standalone build of the Library will create an empty one if it does not already exist.
That file is overwritten by an App Build run-build-script phase, and contains anything the app wants to communicate to the Library build.
Seems kind of complicated, but I'm looking for anything right now. I'll push this to an answer if nothing better comes along.
1 Apps shown are Max OS X. I find command line apps make for simpler tests. Same applies.
2 Cf. Info.plist preprocessing, which I learned about during this investigation.
If you modify your project structure to use a single project with multiple targets then each target's build settings will automatically inherit from the project. From there, you can modify ones that you want to be different, or select an individual setting and press the delete key to set it to the default specified by the project.

Resources