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
Related
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).
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
I am searching for a way to separate the UI tests from the original repository where the main app stays. Is it possible to have the UI test code in another repository, "reference" the main app in some way, and test it?
Starting in Xcode 9, this is possible as long as you're willing to give up a few features. After creating a UI test bundle, you can go to the target settings to specify that it has no target application. (You may also be able to create a UI test bundle without specifying one to begin with.) After you've removed the target application, rather than using the default initializer for XCUIApplication, you can use the initializer that accepts a bundle identifier.
Your tests will end up looking something like the following:
var app: XCUIApplication {
return XCUIApplication(bundleIdentifier: "com.yourdomain.product")
}
func setup() {
app.launch()
}
func test() {
app.navigationBar.buttons["Add"].tap()
}
If you go this route, you will lose the ability to use a few features. Namely:
You won't be able to use Xcode's built in test recorder. The record button is disabled unless you have a target application configured. That being said, you could create an empty app in your project and just switch to the application you'd like to record instead. (That being said, I haven't found Xcode's record feature to be very useful, even for single project apps.)
Xcode won't automatically install your application for you. That usually isn't an issue since is located in a different repository (and sometimes isn't even an Xcode project), so you'll just need to install it before starting your tests. The iOS simulator allows you to drag and drop a .app file, so installing the app is easy enough.
I have 2 test classes in a XCode 5 project:
ABCDataModelTests.{h,m}
- (void)testAlwaysPassing { ... }
ABCDataModelListColorsTests.m which inherits from ABCDataModelTests.
- (void)testNumberOfListColorsGreaterThan7 { ... }
When I ran the test, I noticed that there is a symbol "rT" underneath the subclass's tests as shown in the picture.
What does "rT" stand for? Note that the subclass inherits the test method "testAlwaysPassing."
I can't find anything in the Apple documentation for "New Features in XCode 5/5.0.1" Is there any documentation for what all the symbols stand for?
I found this information on some forums:
The standard way to do things in SenTestingKit/OCUnit/XCTest is to declare your tests in code. If you do, Xcode will discover them statically (ie. not at runtime) using the index. Once Xcode these tests are discovered, they show up in the test navigator with a "t" icon. So far so good.
Now, the SenTestingKit/OCUnit/XCTest frameworks also allow you to create tests on the fly at runtime. Some of our users make creative user of this capability, perhaps to wrap an external testing system or to create tests to represent a dynamic set of data. Xcode cannot discover these tests statically, and only find out about their existence when they are returning resutls during a test run. When discovered, they will show up in the test navigator with a "rT" icon. "rT" being short for "runtime discovered tests".
Finally. If there's anything wrong / unusual about your project that prevents indexing from completing or from properly parsing your test classes, then your tests wouldn't be statically discovered. You may still successfully build and run them, in which case Xcode would end up treating them as runtime discovered tests, and give them the "rT" icon.
Interesting. I have been very annoyed by the same problem with a test class I created by duplicating another file in the IDE. I have duplicated other test files before so that doesn't seem to be the problem.
The test class has only a single test inside it and another side-effect of the purple icon and classification of it as a runtime test is that you can't trigger the test with the little triangle icon offered in the test runner for the other tests or test classes.
The contextual menu in the test explorer offers Test "testBlah" which seems to exercise the test.
Quitting XCode, deleting the xcuserdata folder and rebuilding made the test recognised again as a normal test.
I am getting reminders of older Visual Studio versions which used to have caching problems and needed regular deletion of their local context data!
I agree with what #everyday_productive said
"rT" usually means something wrong with indexing, to fix this go to terminal and type the following:
$ cd ~/Library/Developer/Xcode/
Make sure your Xcode is terminated then delete "Derived Data" folder.
Deleting derived data didn't help me to get rid of running these tests. So I've created a small category on XCTestCase and it worked:
#import "XCTestCase+TestCaseSwizzling.h"
#import ObjectiveC.runtime;
static NSMutableSet* alreadyRunTests = nil;
#implementation XCTestCase (TestCaseSwizzling)
+ (void)load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
alreadyRunTests = [NSMutableSet set];
Class class = [self class];
SEL originalSelector = #selector(invokeTest);
SEL swizzledSelector = #selector(swizzledInvokeTest);
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
method_exchangeImplementations(originalMethod, swizzledMethod);
});
}
- (void)swizzledInvokeTest
{
NSString* selectorString = NSStringFromSelector(self.invocation.selector);
if (![alreadyRunTests containsObject:selectorString])
{
[alreadyRunTests addObject:selectorString];
[self swizzledInvokeTest];
}
}
#end
Delete the references to the files from the project, then re-added them.
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.