Swift test give error "Undefined symbols for architecture x86_64"
Solution 1:
I managed to successfully build and test your package by doing the following modifications:
- renamed the package name to
VnkSwift
, for some reasons the build tool doesn't like dashes in package name, nor it works when you have underscores in the generated package name (so renaming the package tovnk_swift
to make sure the import statement and the package name match didn't work) - renamed the test folder to
VnkSwiftTests
in order for the linker to know what to link against; seems this is a precondition for the linker to know to link against the package - finally, renamed
main.swift
to something else (I usedutils.swift
). The thing is that the presence ofmain.swift
instructs the build tool to generate an executable, and linking against an executable doesn't work very well. After the rename, had to comment theif
code, as global running code can only belong tomain.swift
.
To conclude:
- Avoid non-alphanumeric package names
- Package name and test directory name must be in sync
- Make sure you don't have a
main.swift
file to make sure the package can be linked against
Why can't a unit test target link against executables?
It's because both the test bundle and the executable bundle have global executing code (aka the main function), so the linker doesn't know which one of them to pick. When testing from Xcode, the test bundle runs into the context of the application - it doesn't link against it, like in the situation right here.
Xcode also has this problem when creating a command line tool - you can't unit test that target, if you want to unit test the code then you have to create a library that will be linked by both the tool and the unit test target (or include the files in both targets)
Solution 2:
This seems to be caused my having a main.swift
in your module directory. This results in an executable being build, instead of a library against which your test cases can be linked.
I resolved this issue by splitting my code into two modules. A library for which I have test cases and the application that only contains main.swift
:
Package.swift
Sources\FooBarLib\
Sources\FooBarLib\Something.swift
Sources\FooBarLib\MoreStuff.swift
Sources\FooBarApp\main.swift
Tests\FooBarLibTests\TestSomething.swift
Then in my Package.swift
make sure FooBarApp
depends on FooBarLib
:
import PackageDescription
let package = Package(
name: "FooBar",
targets: [
.target(name: "FooBarLib"),
.target(name: "FooBarApp", dependencies: ["FooBarLib"])
],
)
Then in TestSomething.swift
you import the FooBarLib
module:
@testable import FooBarLib
import XCTest
class TestSomething: XCTestCase {
func testFunc() {
}
}
Solution 3:
In Swift 4, check if your .testTarget
depends on FooBarLib
.
.testTarget(
name: "FooBarLibTests",
dependencies: ["FooBarLib"]),
Solution 4:
Building off of @Cristik's answer, really it comes down to what is in your Sources
folder. For me, I had an folder in Sources
that needed to be exactly the same as what was in Tests
except that what is in Tests
needed to be suffixed with Tests
. So, if you have Sources
-> Foo-Bar
, you had to have Tests
-> Foo-BarTests
. NO EXCEPTIONS.
One other note: all dashes will be converted to underscores so at the top of your test files, you'll have to add @testable import Foo_Bar
.