How to run iOS unit tests with cocoapods that include UI references? - ios

I'm trying to run unit tests on my iOS library that includes cocoapod dependencies. The main library target builds fine, but I get this error when running unit tests:
Unknown Device Type. Using UIUserInterfaceIdiomPhone based on screen size
I believe this is because one of my libraries is running UI tests and that the simulator is not capable of handling them. The crash actually originates from this line of code in one of the pods:
[appearance setItemFont:[UIFont boldSystemFontOfSize:14]];
So how can I exclude a pod from testing so that I can run basic logic tests on my classes that have nothing to do with UI?

It sounds like you want to exclude a Pod from your testing target? This is outlined in the Podfile guide.

Related

Unit tests of xcframework

I made my xcframework but I can't run its unit-tests on iPhone because:
Logic Testing Unavailable Logic Testing on iOS devices is not
supported. You can run logic tests on the Simulator.
I found out it means I have to set host app for my xcframework. So I injected the framework into a host app project - just dragged it. And I got a nested structure with two test targets, one for the host app and the other for the framework. The first one is not needed at this point but the second one still doesn't want to run tests because host app isn't set. I managed to do it by setting these fields in Build Settings:
Test Host: $(BUILT_PRODUCTS_DIR)/HostApp.app/HostApp
Bundle Loader: $(TEST_HOST)
Please note: I did this in the second test target. After that, Xcode allowed the tests to run, but they hang every time they run. And a warning appeared:
/Users/xxxxxxxxxx/MegaApp/Framework/Framework.xcodeproj Unable to find
a target which creates the host product for value of $(TEST_HOST)
'/Users/xxxxxxxxxx/Library/Developer/Xcode/DerivedData/HostApp-xxxxxxxxxxxxxxxxxxxxxxxxxx/Build/Products/Debug-iphoneos/HostApp.app/HostApp'
This looks like Xcode expects framework's main target to produce the binary of the host app I specified in its test target. But framework's main target does the other work in accordance with its purpose. And I got stuck here.
Anyway, is it something that can be easily fixed or it's impossible to run tests for xcframework on device?

Xcode: merge Unit tests into a single target

I'm developing a modular app with >30 modules, where each module has its own Unit test target. I also have a special test scheme which runs all Unit tests from all the targets.
The problem is - it's too slow. While the tests run fast, it takes a lot of time for Xcode to switch from one test target to another. The "all tests" scheme is supposed to be run in CI so I would really like to improve the performance.
I tried to convert all Unit test targets into static frameworks and link them to a new "merged" Unit test target. In this target I then created a single AllTests.swift file importing the frameworks and manually invoked tests on each of them. The performance improvement was ~4x.
However, this will be quite hard to maintain this file. My assumption is, since they are linked statically, there can be a way to make Xcode run imported in such a way tests automatically. If this works I'll be able to fully automate this process, keeping test targets for development and generating the "merged" target in CI.
What else I tried:
Investigated the option of using SourceKitten to parse the project and generate the AllTests.swift file automatically. This would require building the project one more time, eliminating the performance improvement.
Instead of linking frameworks, include references to source files into the merged target. This introduces a bunch of "multiple files with the same name" and "same declaration" errors that cannot be automatically resolved.
Made all declarations in the static frameworks public.
Googled a lot. This SO question is quite close but the solution doesn't work for me.
So the main question is - is there a way to make Xcode automatically include Unit tests that are part of a static framework the current target is linked against? Or any other suggestion on how I can automate this process will be very much appreciated.

iOS Xcode SPM failed to demangle superclass

My app is composed of many projects (frameworks), one for each main feature and a common framework with all sorts of things that I need to access in multiple of my features.
I'm using Xcode 11's Swift Package Manager to add dependencies.
The common framework contains a RxSwift dependency, which I use throughout the whole project.
I'm facing problems when I try to use RxTest in any of my feature frameworks.
If I add RxTest via SPM to the test target directly and run the tests, I get
failed to demangle superclass of 'class name' from mangled name 'other class name'
and many
Class 'class name' is implemented in both 'common framework path' and 'test target path'
where all these classes are Rx related. The 'failed to demangle' error crashes the test and only occurs when I try to initialize a RxTest class.
If I add RxTest to the common framework, the tests run fine, but when I run the app, I get
dyld: Library not loaded: #rpath/XCTest.framework/XCTest
Which makes sense, because I'm adding a test framework to a non-test framework, and it's not something good to do.
So basically, I wasn't able to get a configuration where both the tests and the app run fine. Either the app runs or the tests run.
How can I get this working? Is there a way to include RxTest on the common framework only when I build it on a test target? Or should RxTest only be included on the test targets and I'm missing some configuration?
Xcode with SPM dependencies cannot handle same SPM dependency in multiple targets that are dependent on each other right now. Each dependency needs to be only in single target at the moment. I dont know why as of now, but I'll try investigate more and file bug if it is not filed yet.
Your issue is likely that the library is using static linking instead of dynamic linking. In SwiftPM you can specify a library as being static or dynamic if you want or you can just let the build system decide which is what most packages do. Xcode seems to favor the static approach when it builds with SwiftPM which results in the build issues you are experiencing.
If you modify the Package.swift to have RxTest be a dynamic library instead it should work. You can easily test this by cloning RxSwift and modifying this line:
.library(name: "RxTest", targets: ["RxTest"]),
into:
.library(name: "RxTest", type: .dynamic, targets: ["RxTest"]),
and then dragging the local copy of RxSwift into your Xcode Project Navigator.
It will then use your local copy of the package instead of the one cloned by Xcode.
Once you do this you can link it against any targets you need and it should work. If that does actually fix the problem then your long term solutions are likely:
1) Have a fork that simply changes it to a dynamic library.
2) Convince the RxSwift community to change their products to dynamic or to vend dynamic versions in addition to the default.
3) Don't use RxTest or similar things in multiple places.
It is also worth noting, that Xcode 11.3 and earlier do not support archiving with dynamic Swift Packages. So if you go down the dynamic route you will have to wait for Xcode 11.4.
Workaround:
I had the same issues. My project configuration is:
Workspace
ProjectAppOne
AppTargetOne (Embed and sign FrameworkTarget)
ProjectAppTwo
AppTargetTwo (Embed and sign FrameworkTarget)
ProjectCoreFramework
FrameworkTarget
testsTareget
SPM Dependencies (including RxSwift)
My workaround was:
Duplicate the FrameworkTarget and create a FrameworkTargetT (make sure that are both building with no issues).
FrameworkTarget is still used to build the applications targets, so be sure that is the only one used in the imports of the application targets.
Add RxTest to FrameworkTargetT
Remove RxTest from FrameworkTarget
Set in your tests #testable import FrameworkTagetT
Adjust the FrameworkTarget to not run the testsTarget
Set the FrameworkTargetT to run the testsTarget
Adjust your CI fastlane/scripts, that involve testing target and call the FrameworkTarget directly
At the end the project structure was looking like:
Workspace
ProjectAppOne
AppTargetOne (Embed and sign FrameworkTarget)
ProjectAppTwo
AppTargetTwo (Embed and sign FrameworkTarget)
ProjectCoreFramework
FrameworkTarget
FrameworkTargetT
testsTareget
SPM Dependencies (including RxSwift)
I do not think is the ideal solution, it is easy to import the wrong framework in code or test, so double check that you are happy with that. But while we are waiting for the SPM team to sort the problem, it could be used and removed easily. This is also easy if you have already a framework separated code.
PS: you need to remember to add every new file to both of the targets FrameworkTarget and FrameworkTargetT.

CocoaPod and Unit Tests for Swift Framework

So I'm having trouble locating the definitive answer after trawling Cocoapods.org
I know Quick and Nimble are used for Unit Tests, at least for objective-C, however when creating a CocoaPod in Swift as a framework, I read somewhere there is only one choice, but what is that choice?
What is THE framework I have to use for my swift,framework cocoa pod to have it count as tested, as it's not XCTest obviously.
For a pure Swift CocoaPod you can use Quick/Nimble or XCTest. I recommend reading the excerpt here from CocoaPods.org in regards to creating a new Swift framework (including unit tests).
https://guides.cocoapods.org/making/using-pod-lib-create.html
I recently created a Swift CocoaPod using the "pod lib create" command and using only XCTest. Using the provided command line approach you can start a new project to include unit tests and a demo app, if desired and all the setup is handled for you. The only configuration I needed was to ensure the pod target had enable_testability = YES so that your unit tests can read your pod classes.

Unit Testing Issue in Xcode

I have recently integrated XCTest into my project. It works fine when I run test cases using the play button in source editor or Using the play button in test navigator. My problem is, when I use the Test Button (The Spanner like symbol) in the ToolBar I am getting compilation errors.I already have integrated pods in my project and there is a static library created by me as well.
Note : During thorough checking I figure out all the compilation errors coming in the static library created by myself.The error count is too large so compiler shows "too much error" message
Is there any additional setup needed for including the static library into tests?
This issue arise only in case of testing I can succesfuly build and run the project
Thanks
Test Succeeded Here
Compilation Error Here
Try doing a clean build to see if xcode hasn't cached anything when you try to do a full test.
Got the solution finally, the issue is because of test target added as part of my custom library. When I try to run the test from toolbar xcode try to run all the unit test targets (My main project's test and my custom library's test case ). Library's test case can't run independently because it has some coupling with main project. Remove the test target and run test cases everything perfect.

Resources