XCTest for a Mac command line application - xcode

I want to test a command line application, but
import XCTest gives me a message that the underlying module cannot be loaded.
How can I test a simple "hello world" command line App?

So, you have a simple command line app with main.swift/main.m and maybe some other code.
By default you have only one target with the same name as your product.
Press the "project" file, and you'll get smth like this:
The blue icon in the left-top corner is your project's file, and in the right-bottom part you have a list of targets.
Press the "+" button, and select MacOS Unit testing Bundle:
Name your testing bundle somehow, e.g. test:
Now your project contains 2 targets: one "main" (named the same as the project) and one "testing".
When adding a new file, don't forget to add it to the testing target (the checkbox under the Targets section ):
Please note that this type of testing target is Logic tests, not Application tests, for more info look here: https://forums.developer.apple.com/thread/52211

After adding unitest for macOS command line we will end up with many linking errors. to avoid that,
For adding unit test target (XCTest) for Mac OS Command Line project which has(main.swift and other swift files), to make this work,
Add UnitTests target to scheme by editing in manage schemes
Make your functions and classes has PUBLIC access
This solved all my linking errors.Hope it helps you as well

Related

Xcode XCUItest LaunchTests - how do they work

If I create a new Xcode project with Xcode 14 with checked 'Include Tests' checkbox it creates 2 files in the UITests folder:
I am interested in the second one: the [Project]LaunchTests.swift file.
There is this automatically generated code:
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
If I run this test from the diamond in the code, it runs 4 tests that I can view in the report navigator:
Xcode runs these 4 tests, but I didn't define them anywhere.
Question: where can I find the definition of that tests? Is this kind of an internal testplan which is associated with the LaunchTests file? Where can I find more information about this? It looks like there is a way to run tests with changing light/dark mode and changing orientation without writing a line of code.
Thanks in advance.
If you don't want the four variants of the test to run, then do not (as the template does) return the runsForEachTargetApplicationUIConfiguration value for this test class as true.
As the documentation tells you, when this is true, the test runner consults your actual app target to see what variants it has (light and dark mode, orientations, language localizations).

Cocoa Unit Testing - App is launching before tests [duplicate]

When I run my tests in XCode 5, the main window of my OS X app appears on the screen for a couple of seconds while running the tests. Why? Even if I uncomment all my tests it still opens my main window.
You are running application test, not logic test. This means an instance of your app will be started and then run the unit tests. This allow you to perform some integration test that require your app is running.
Here is the guide to setup application test and logic test.
If you want to change it to logic test (so it run faster and don't need to start your app first):
go to build settings for your unit test target
search Bundle
remove Bundle Loader and Test Host
Thats right, you have to delete the "Bundle Loader" and "Test Host" from your build settings.
But you have to add the necessary implementation files to your unit test target. The necessary files are what you want to use in your unit test cases. You need to do this because in logic tests XCode wont compile the whole application. So some of your files will be missing.
This is en error message if you have left out a file:
Undefined symbols for architecture i386:
"_OBJC_CLASS_$_Module", referenced from:
objc-class-ref in Lobic Network.o
objc-class-ref in Logic_Unit.o
ld: symbol(s) not found for architecture i386
clang: error: linker command failed with exit code 1 (use -v to see invocation)
You can add the missing files by selecting the implementation file and bringing up the file inspector. There will be a section named "Target Membership" and there you can set the files target membership to your unit test also.
With XCTest, application files DO NOT need to be included within XCTest targets. The XCTest bundle is linked against the application which makes those files available during runtime.
To make this work, ensure the compiler option "Symbols hidden by default" is set to NO Within the Application target.
Here is a blog post with screenshots for clarity:
http://zmcartor.github.io/code/2014/02/24/slim-xctest-targets
The advantage of this approach is test target builds much much faster.
In XCode 7, removing Host Application does not work for me. Indeed I use the following to avoid app runs.
Setup Test Scheme Arguments
in main.m
static bool isRunningTests()
{
NSDictionary* environment = [[NSProcessInfo processInfo] environment];
NSString* testEnabled = environment[#"TEST_ENABLED"];
return [testEnabled isEqualToString:#"YES"];
}
modify main()
int main(int argc, char * argv[]) {
#autoreleasepool {
if (isRunningTests()) {
return UIApplicationMain(argc, argv, nil, nil);
} else {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
}
If the tests are for code that can run on desktop and mobile, you can run them without a simulator or hosting them within your app.
The trouble is that you cannot use the scheme chooser for your normal target (desktop or iOS) to run the test.
The following worked for me in Xcode6.
File > New Target...
Select Cocoa Testing Bundle from the OS X category.
Take care to select None from the target drop-down.
Click Finish. Add the relevant files to the new target as described above.
Now create a scheme to run the test.
Click the schemes chooser top-right and choose New Scheme..., click the drop-down and navigate down the list to the new target. Now you can choose the scheme from the schemes chooser, and use ⌘U to run the tests.
I just wasted a morning on this.
Project was created in XCode 4 and used SenTesting.
Tried migrating tests on XCode 5/XCTTest
Had same issue - app ran in simulator and test never started
after trying everything (change from app to logic tests, change to XCTest, remove SenTesting)
gave up created a clean XCode 5 project.
Added all my files in and tests ran ok.
May still have issues with Storyboard as these were built with XCode 4.
Drastic but it works so keep it as last resort.
On XCode5, the app does start. This answer shows how to change its delegate when running unit tests so that it exits right away: https://stackoverflow.com/a/20588035/239408

Process.Start within application bundle on Mac (using MonoMac)

I am using MonoMac do develop a very simple application for Mac. I have a solution consisting of two projects: the main project, and the auto updater. The main project references the auto updater, so that the auto updater becomes a part of the application bundle of the main application.
When running the main application, I want to launch the auto updater if there is a new update. On windows, this is a simple Process.Start("C:\path\to\updater.exe"); On Mac, however, this seems to be problematic. When doing it the above mentioned way (both when using absolute and relative paths to the updater.exe), nothing happens, and debugging simply says that the process has been ended already when I try to get any information.
I have attempted to launch the updater using AppleScript:
do shell script "mono /path/to/bundle.app/Contents/MonoBundle/updater.exe"
But this produces the error "No Info.plist file in application bundle or no NSPrincipalClass in the Info.plist file, exiting". This same error appears when I try to start the main executable the same way, and I know that the Info.plist is configured correctly for this (since I am indeed able to start the app), so there must be something else that is wrong.
Am I doing something obviously wrong? I just want to start the updater from the main executable, how I do it doesn't matter.

Mac OS X: How to launch an application (.app) from a "Command Line Tool" type of app?

In Xcode 4.6, I created a new application based on the "Command Line Tool" project template.
How can I programmatically start another application (.app application bundle) from that "Command Line Tool" app?
There are numerous ways to accomplish this, using Launch Services and or NSWorkspace.
One of the more flexible ways to identity a bundled application is via its bundle identifier (CFBundleIdentifier), which is a string like com.apple.TextEdit. This allows you to identify an application without having to hard-code an assumed path where the application will be found, or by hard-coding the name of the application bundle, both of which a user could easily change. You can use NSWorkspace's launchAppWithBundleIdentifier:options:additionalEventParamDescriptor:launchIdentifier: to launch the app. If you don't already know it, you can obtain the bundle identifier of an application bundle by checking its AppName.app/Contents/Info.plist file. Then use the following code:
if (![[NSWorkspace sharedWorkspace]
launchAppWithBundleIdentifier:#"com.apple.TextEdit"
options:NSWorkspaceLaunchDefault
additionalEventParamDescriptor:NULL
launchIdentifier:NULL]) {
NSLog(#"launching app failed!);
}
Important: NSWorkspace is part of the AppKit.framework framework, which is not initially included in the "Command Line Tool" project template. To add it to your project, select the target in the list of targets like shown in the image below, and click the + button to add additional frameworks.
Add both AppKit.framework and Cocoa.framework.
That will result in all 3 being listed in the Link Binary With Libraries step. At that point, you can remove both the Foundation.framework and AppKit.framework from the linking stage, and leave just the Cocoa.framework, like below:
Have you tried "open"? At least in terminal "open" runs files and/or apps.

Xcode: is there a way to get the bundle identifier of a parent project?

I have a Xcode project for a mac app that contains another project for a helper app to launch the main app at login. Is there a way I can base the bundle identifier of the helper app off of the main app with a project variable like ${PRODUCT_NAME} but something like ${ROOT_PRODUCT_IDENTIFIER}?
So the main app's bundle identifier would be:
com.mydomain.app
and the helper app's bundle identifier would be:
${ROOT_PRODUCT_IDENTIFIER}.Helper → com.mydomain.app.Helper
My goal with this is to create a really easy to use generic launch at login helper app that any mac app can use, I've got it working in this repo but it requires a couple values to be changed: https://github.com/kgn/LaunchAtLoginHelper
I'm afraid the variable you're looking for doesn't exist, have a look here or here
But I can think of one workaround by creating your own environment variable, which isn't difficult. In order to achieve what you want, create a build script for your main project that extracts the identifier and saves it to an environment variable.
Go to your main project's target and click on Add Build Phase -> Add Run Script.
In the shell box that appears, type this in:
ROOT_PRODUCT_IDENTIFIER_PLIST=`/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" $INFOPLIST_FILE`
export ROOT_PRODUCT_IDENTIFIER=`eval echo $ROOT_PRODUCT_IDENTIFIER_PLIST`
Now go to your helper's project and reference $(ROOT_PRODUCT_IDENTIFIER) in its plist/build settings.
Remember to add your main project as a dependency for the latter, so that variable is always set by the time its needed.

Resources