33

I want to test drive some Swift examples using XCTest from the command line if possible.

import XCTest

class LeapTest : XCTestCase {

    func testVanillaLeapYear() {
      let year = Year(calendarYear: 1996)
      XCTAssertTrue(year.isLeapYear);
    }
}

I'd love to run it from the command line.

I already set Xcode to use the developer tools in the beta:

sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer/

If I naively try and run it it goes like this

$ xcrun swift LeapTest.swift
LeapTest.swift:1:8: error: cannot load underlying module for 'XCTest'
import XCTest
       ^

Any way to run it directly from the CLI? Or do I have to create a Xcode project?

John Paul Ashenfelter
  • 3,135
  • 1
  • 22
  • 29

2 Answers2

49

I think the issue is you have your test.swift file under the main project's target membership. Make sure your swift test files belong to the Test target only.

chourobin
  • 4,004
  • 4
  • 35
  • 48
  • 2
    So it needs to be an Xcode *project* for this, right? I was trying to run it like any other CLI script with tests (e.g. python, ruby, etc) – John Paul Ashenfelter Jun 24 '14 at 13:56
  • Just to see if it helps other people: What worked for me is to set the test files to belong to the test target and the classes you are testing should be set to belong to the test target as well as the project target. – Franklin Nov 07 '14 at 22:01
  • 1
    @JohnPaulAshenfelter my mistake, you can refer to Adam's answer above to run LeapTest.swift from CLI. You must add the framework search path to xcrun swift: `xcrun swift -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks LeapTest.swift` – chourobin Jul 07 '15 at 16:19
  • Although XCode resolves the linking issue for you if you follow the workflow of creating a test target (https://developer.apple.com/library/prerelease/ios/recipes/xcode_help-test_navigator/AddingTestBundles/AddingTestBundles.html), there should be a way to manually resolve the link issue so you can put your unit tests where ever you want. – Lance Kind Feb 28 '16 at 18:09
15

I was able to get your XCTestCase compiling with the following command:

swiftc \
-F/Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
-Xlinker -rpath -Xlinker /Applications/Xcode6-Beta5.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks \
-lswiftCore LeapTest.swift \
-o LeapTests

Then you can execute the tests with xctest:

xcrun xctest LeapTests

And to break down those swiftc command line options:

  1. -F... adds XCTest.framework to the framework search paths, enabling it to be imported from Swift
  2. -Xlinker -rpath ... makes sure the XCTest shared library can be found at load time
  3. Without explicitly specifying -lswiftCore, I found that xctest would crash when it tried to run the test suite

Hope that helps!

Adam Sharp
  • 3,618
  • 25
  • 29
  • Very helpful, thanks! How do I combine two or more swift files in this case, though? Or, if LeapTest imports another class, how do I tell it where the class is? – Metaphox Nov 24 '14 at 12:24
  • @Metaphox Maybe try `*.swift`, or just listing all of the file names at the command line? – Adam Sharp Nov 25 '14 at 01:56
  • Yes I tried that... but, say I have foo.swift and bar.swift, and I try to import foo from bar, I'll get error "bar.swift:3:8: error: no such module 'foo'". – Metaphox Nov 25 '14 at 09:53
  • @Metaphox in this case they should actually be in the same module, so I'd guess there's no need to `import` – Adam Sharp Nov 26 '14 at 04:53
  • well, that, too, failed :) without `import` – Metaphox Nov 27 '14 at 09:02
  • @Metaphox hmm what if you add the `-emit-library` option? Also what kind of failure? Sorry, just guessing at this point :) – Adam Sharp Nov 28 '14 at 10:09
  • well, in foo.swift I defined an extension to Array. Then, in fooTest.swift, I would like to test the extension. If I compile foo.swift with `xcrun -sdk macosx swiftc -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -lswiftCore -emit-library -module-name foo foo.swift`, I can get a `libfoo.dylib`. How may I import this lib in fooTest.swift then? – Metaphox Nov 28 '14 at 12:12
  • And if I compile everything with `xcrun -sdk macosx swiftc -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -Xlinker -rpath -Xlinker /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -lswiftCore foo.swift fooTest.swift`, the error is that in fooTest.swift the function which is defined in that extension can't be found. Btw many thanks for the discussion :) – Metaphox Nov 28 '14 at 12:18
  • 4
    @Metaphox got something working! Here's a sample project, let me know if this helps: https://github.com/sharplet/minimal-swift – Adam Sharp Dec 05 '14 at 07:55
  • It's great to see how to do this from the command line. Apparently XCTest is hiding in /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/L‌​ibrary/Frameworks. There should be a way to use XCode and add these -F and -Xlinker dependencies too. If you know how to do that, please comment. – Lance Kind Feb 28 '16 at 18:13