Xcode 7: Can't create a Swift command line tool with a unit test target - xcode

Oh, Xcode!
I'm stymied. If I create a Mac OS Cocoa app, I get a unit test target for free. But I want to write a command line tool (or even a library that I can link into a command line tool) and write some unit tests against it. I'll be darned if I can figure it out.
I've gotten far enough that I have a command line target, and a test target. I can "#testable import" my commnd line module and use the code in the test code with no errors in Xcode. However, as soon as I try to run my test (Cmd-U), I get a link error. None of the classes in the main module can be linked. What?
I've been messing with this for hours, and the Great Google has been no help. (I'm using Xcode 7, and Xcode 6 seems very different.) Can anyone help me?
Thanks.

So far the only solution I've found for this problem is to manually add all the files containing code that you want to test to unit test target manually:
This is something you wouldn't do when testing an application target. I think the fact that the command line target cannot be selected as the test host for a unit test target might be related with this issue:
Another option you have, which might require a bit more work, is to define all your logic into a Framework, and write the command line app as a consumer of the APIs it provides.
This way you can easily unit test the framework the usual way, and then write integration tests for the command line app in the form of scripts that call it and assert the results.

After struggling with this here is the solution that worked for me:
Step 1: Add a testing bundle. Editor > Add Target, Cocoa Testing Bundle
Step 2: Edit the scheme. Product > Scheme > Edit Scheme. Select Test, click +. Under “Choose targets to test as part of this scheme”, select your test target.
Step 3: Try with you simple test

There are cases when adding the same file to both utility and the test target is not possible (some kind of loops resulting in redefinitions).
However, it's possible to create an additional application target, add all files of the utility except for main.swift to the app, and then use this app as a hosting app for the tests (and also enable "Allow testing Host Application APIs checkbox).

Related

Writing XCTestCase for Mac OS Command Line Tool

I am trying to unit test a command line tool written for Mac OS.
When I first create the project, XCode does not generate a tests group in the project navigator. When I try to add a new test target, it doesn't give me the option to specify my target as the "Target to be Tested".
My question is this: is it even possible to use XCTest for a Command Line Tool project? Or is it just considered trivial to do so by virtue of the fact that I could just run it from the command line? I could understand that reasoning, but there is internal functionality I'd really like to test.
I'm not sure what version of Xcode you are using, but I ran into a similar problem using the templates in Xcode Version 6.2 (6C131e). That said, I was able to get XCTests to work with a Command Line Tool project. The solution was to ignore the "Target to be Tested" field during creation and instead add the Test target to the main scheme after creating it:
Go to Manage Schemes. You should see your main Target’s scheme and a
newly created test scheme.
Select you main scheme and go to Edit.
Select the Test action and add your new Test target to the tests
list using the “+” in the Test action detail panel.
From there you should be able to run the tests using cmd-U.
In Xcode 8.2, I was only able to run unit tests in a command line app by adding a unit test target from the Test Navigator, then editing the testing scheme to include that new test target under the "tests" list, and manually adding testable source files to the test target from the "target membership" section of the File Inspector pane.
(Adding a unit test target from the project "add target" screen wouldn't link properly with XCTest framework, even after adding the framework to build phases.)
Following the Apple doc directions for adding a unit test target from the Test Navigator pane looks like this:
Note:
In the Unit Test Target setup, the "target to be tested" dropdown still won't select the command line tool. Leave this option as "None".
For adding unit test target (XCTest) for Mac OS Command Line project which has(main.swift and other swift files), to make this work,
1.Add UnitTests target to scheme by editing in manage schemes
2.Make your functions and classes has PUBLIC access
This solved my linking errors. Hope it helps for you guys as well

How to use 'SQLite' (the wrapper on github) in a Command Line Tool?

I'm successfully using the great Swift wrapper for sqlite from https://github.com/stephencelis/SQLite.swift in a Cocoa application.
But when I try to use the wrapper in a Command Line Tool project and follow the same detailed installation steps I get the following error:
Check dependencies
Unable to run command 'PBXCp SQLite.framework' - this target might
include its own product.
I checked the dependencies, but couldn't figure out how to solve this.
You can't link dynamic framework (a .framework) with your app in a Command Line Tool project. The reason is simple — a command line tool target builds a single binary file. This is unlike a regular Cocoa application, where the .app "file" is actually a folder containing .frameworks and other stuff inside.
So basically you'd have to build a static library instead (one that links with your app's binary during compilation) — except that as of Xcode 6.1 it's not possible yet with Swift.
So the only thing you can do — AFAIK — is add the SQLite.swift's source code directly into your own app target (so it compiles together). It's ugly, but works.

How do I add a target to a test scheme?

I have configured unit testing for a project, and I need to use the classes that are in the run scheme also in the test scheme.
This because if I try to use a class that I use in the run scheme, I get a linker error, the linker does not recognize that (user defined) class.
I called the test bundle "TestBundle", I am able to test the application unless I use other classes. This is what I see when I try to edit the test scheme:
It seems like there isn't a place for adding another target, how do I do that?
schemes RUN targets... they don't manage what a target is made up of.
they could also be called 'environments' or 'execution setups' or so :)
targets are managed under your project (when you click onto the blue project icon in the project navigator)
note: I think that apple brought us schemes only to haze us ;D

How to set up working logic unit tests target in Xcode 4.5 "Command Line Tool" project?

Having trouble getting unit tests set up for a specific scenario. Here's what I'm trying:
In Xcode 4.5, I created a simple OSX "Command Line Tool" application project (Foundation).
Note that Xcode does not provide the option to add unit tests to a "Command Line Tool" project automatically – so please don't suggest ticking the ticky-box; it ain't there :-/
In my project, I created a trivial example class that I'd like to test; e.g. "Shape".
I followed instructions in Apple's Xcode Unit Testing Guide for Setting Up Unit-Testing in a Project:
I added a unit test target to my project, and
I edited the "Test" scheme to run the tests in the new target.
In the test project's implementation (.m) file, I added an import for Shape.h and code in the setUp() method to instantiate a shape and assign it to an instance variable.
At that point, I decided to see if things would build and if the default test would run still. However, when I selected Product...Test from the menu, the build failed with the following error:
Undefined symbols for architecture x86_64:
"_OBJC_CLASS_$_Shape", referenced from:
objc-class-ref in ExampleTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Interpreting this error is not the issue. I grok that the unit test target isn't being linked to the binary containing Shape's implementation. However, I don't (yet) grok Xcode unit testing & target configuration. So:
What do I need to do in order to get the test target linking against the command line tool's resulting output? Can I link to a command-line executable from the unit test target? Apple's documentation looks specific to regular OSX applications (*.app) and iOS applications, and this is neither.
I have business logic classes that I'd like to develop in a command-line tool setting (to begin with), so I'd like to understand what I need to do to get a unit test target running in a "Command Line Tool" type of project. Thank you!
(p.s. Note that I'm not interested in running my unit tests from the command line – Stack Overflow already suggested "similar" questions on how to do that – but rather running unit tests on a "Command Line Tool" type project, and still from within Xcode.)
I've determined a workaround I find suitable and which doesn't appear to have significant drawbacks, other than an added target.
In a nutshell: the solution involves adding a static library target to take advantage of Xcode's ability to create & run unit test code around such a target. The command line tool target then delegates to the static library, where an alternate main()-like function is defined and called by the real main() entry point. The command line tool is kept free of non-trivial code, so the unit test target can access everything worth testing.
Here are the steps:
From an empty Xcode, from the menu choose File...New Project.
In the resulting dialog, chose OS X...Application...Command Line Tool. For the purpose of this example, I'll assume it is named SampleCmd.
After the basic command line tool project has been created:
From the menu choose File...New...Target.
In the resulting dialog, choose OS X...Framework & Library...Cocoa Library. For the purpose of this example, I'll assume it is named SampleCmdLogic.
Choose type Static, so the command line tool will remain a stand-alone executable.
Make sure the Include Unit Tests box is checked.
After the static library project has been created:
Copy the main() function from main.m to SampleCmdLogic.m, replacing the #implementation block. (This file will hold the main entry point only. Other files can be added for Objective-C classes, etc.) Rename the function to libMain().
In SampleCmdLogic.h, add a declaration for the new libMain(), replacing the #interface block:
int libMain(int argc, const char * argv[]);
In the command line tool's main.m, add an #import "SampleCmdLogic.h" at top.
In the command line tool's main.m, change the entire contents of the real main() function to:
return libMain(argc, argv);
The code is now ready, but there are required linking steps:
In the project settings for SampleCmd, under Build Phases, expand Target Dependencies and add (+) SampleCmdLogic as a dependency.
In the project settings for SampleCmd, under Build Phases, expand Link Binary With Libraries and add (+) libSampleCmdLogic.a
Everything is now ready. When you change to the SampleCmd target and choose Product..Run from the menu, the build should succeed and output generated as expected. When you change to the SampleCmdLogic target and choose Product...Test from the menu, the build should succeed and the unit tests will run. The single issue reported will be the initial default failing unit test assertion inserted by Xcode, in SampleCmdLogicTests.m. This is expected.
From this point on, proceed to add all logic and corresponding tests to the SampleCmdLogic target. The SampleCmd target is to remain trivial and providing the command line tool entry point only.
There are normally a number of additional steps to add a test target to an app project -- in particular, the setting of Bundle Loader and Test Host as I describe in https://stackoverflow.com/a/12624873/246895.
But when I did them with a Command Line Tool and tried running tests, all it did was run the tool. For an app, it goes through a phase of launching the app, injecting the test bundle into the running app, then executing the tests. But these phases don't apply to Command Line Tools.
So instead of an injected test bundle, what you'll need is a second command-line tool that runs your tests. Then set the your classes so they target the test tool as well as your actual tool. gh-unit and google-toolbox-for-mac both follow this model, so I'd try them.
It seems like the most immediate problem is simply that Shape is not included in the new test target. Try adding Shape.m to the test target:
Click on Shape.m in Project Navigator
Open the Utilities view (View -> Utilities -> Show Utilities)
In the Target Membership section of the Utilities view, make sure that both your app and test targets are checked.
I don't know if that's the end of your issues with your setup, but it seems like a likely candidate for your most immediate problem.

Do I have to do something to get OCunit tests to compile out of box with XCode 4?

I just installed XCode 4 today (using Apple mac app store), and I created a new Mac OS application, and tried to switch to test mode,and build the test skeleton code it generated for me. It failed here:
In mytests.h:
#import <SenTestingKit/SenTestingKit.h>
The error is:
file://..mytests.h: error: Lexical or Preprocessor Issue: 'SenTestingKit/SenTestingKit.h' file not found
Now, when I use locate from the terminal to find SenTestingKit.h, I notice it exists under the /Developer-old/Library/Frameworks folder (which is what XCode 4 installer renamed my /Developer folder to). There is no new /Developer/Library/Frameworks. And I can't seem to find SenTestingKit.framework on my disk, other than the developer-old one.
What's up? It seems SenTestingKit.framework is not shipped with XCode 4.
Update:
Furthermore, When I copy my old SenTestingKit framework from XCode 3 into /Developer/Library/Frameworks, it sort of builds, but it doesn't work the way I would expect. The dummy test is designed to just fail, but when I "run test", I just get the normal cocoa app document window opening, and no indication that my test has failed (as I intend it to do).
This is pretty bad. I can't get a Unit test to FAIL. That's not the usual situation for me, you understand.
You may find that the reason for this is because you've installed Xcode 4 into a directory such as /Xcode 4.x/ or similar. The space is causing the problem because the -I paths are "Xcode" and "4.x/Library/Frameworks".
To fix this, what you need to do is select your test target, and under its build settings go and find the Framework Search Path, and put quotes around the two arguments, so you have:
"$(SDKROOT)/Developer/Library/Frameworks" and "$(DEVELOPER_LIBRARY_DIR)/Frameworks"
Then you default tests will compile, link and fail.
You will maybe need to import the framework into your project. Otherwise, the header file won't be recognize.
If you can't see the framework you're looking for, you can adjust the framework look paths in your project's build settings.
When you're building unit tests in the same project as you main code, make sure that XCode 4 didn't automatically connect you mytests.m file into the "Compile Sources" section of your main code.
For example, if you have two targets in our project:
MyProject
MyProjectTests
Check the Build Phases for MyProject to see if XCode added mytests.m into the "Compile Sources" accordion. This will cause your builds to fail because SenTest isn't included in the main project.

Resources