We have the same issue.
There is a "common test utils" package, which is a collection of all kinds of mocks for other packages used in the project. It's quite handy to plug these common test utils in the test target.
For example, let's say we have Accounts package, that has the Accounts and AccountsTests targets. The there's TestUtils package with TestUtils and TestUtilsTests targets.
TestUtils target depends on Accounts target.
AccountsTests target depends on TestUtils target.
In terms of targets there's no circular dependencies as such.
🎯AccountsTests ---> 🎯TestUtils ---> 🎯Accounts
But in terms of packages, there is a circular dependency:
📦Accounts (🎯AccountsTests) ---> 📦TestUtils (🎯TestUtils) ---> 📦Accounts (🎯Accounts)
Swift PM only support package-level dependencies.
This seems to be the design philosophy or an oversight, I can't really tell.
We've been building these packages so far using:
- Just good old Xcode projects created manually
- Buck build system
- CocoaPods
Both Buck and CocoaPods (and I assume Bazel too) allow this kind of dependencies.
With Buck I'd have to use apple_library and apple_test rules, each of those rules would define a target.
apple_test will depend on apple_library.
These 2 targets together are part of one logical package, but both targets specify the dependencies they require.
There's no package-level dependency specification.
# Modified example, not actual Buck rules, but an example.
apple_library(
name = "Accounts",
deps = [
library("SomeOtherDependency"),
# ---> Does not depend on TestUtils!
],
tests = [
dependency("AccountsTests"),
],
frameworks = [
system_framework("Foundation"),
],
)
apple_test(
name = "AccountsTests",
deps = [
library("Accounts"),
library("TestUtils"), # <--- Depends on TestUtils
],
frameworks = [
developer_framework("XCTest"),
system_framework("Foundation"),
],
)
apple_library(
name = "TestUtils",
deps = [
library("Accounts"), # <--- Depends on Accounts
],
frameworks = [
developer_framework("XCTest"),
system_framework("Foundation"),
],
)
CocoaPods, even though we no longer use it, allows similar target-level dependency specification, for example:
s.test_spec "AccountsTests" do |test_spec|
test_spec.source_files = "Tests/**/*.{h,m,swift}"
test_spec.resources = ["Tests/**/Fixtures/*"]
test_spec.dependency "TestUtils" # <--- Like this
end
# TestUtils will depend on "Accounts" in its turn.
Both Buck and CocoaPods can generate an Xcode project, that can be used to build and test all the targets.
Swift PM allows only package-level dependency.
It looks at packages only and doesn't take targets into account.
Also, with Xcode 11.2.1 it still crashes while trying to resolve circular dependency.