Hello,
During the migration process from an Xcode-managed project (My-Project.xcodeproj
) to a Tuist-managed project (My-Project-Tuist.xcodeproj
), I’ve encountered an issue with migrating tests.
When tapping the “Test” button in Xcode, I receive the following alert:
Scheme “My-LondonApp” is not testable.
Tuist Version:
[tools]
tuist = "4.43.2"
Directory Structure:
MyRepo/
├── Tuist/
│ ...
│
├── Tuist.swift
│
│
├── Projects/
├── App
│ ├── Project.swift
│ │
│ ├── My-Project-Tuist.xcodeproj
│ ├── My-Project
│ ├── App
│ │ ├─ Resources
│ │ │ ├─ Images
│ │ │ ├─ ...
│ │ │ ├─ info.plist
│ │ │
│ │ │
│ │ ├─ Sources
│ │ │ ├─ Application.swift
│ │ │ ├─ ...
│ │
│ │
│ ├─ Tests
│ ├─ TestPlans
│ │ ├─ AllProjectTests.xctestplan
│ │ ├─ IntegrationTests.xctestplan
│ │ ├─ UnitTests.xctestplan
│ │
│ ├─ UnitTests
│ │ ├─ Resources
│ │ │ ├─ ...
│ │ │
│ │ ├─ Sources
│ │ │ ├─ ...
│ │ │
│ ├─ IntegrationTests
│ │ ├─ Resources
│ │ │ ├─ ...
│ │ │
│ │ ├─ Sources
│ │ │ ├─ ...
│ │ │
│ ├─ UITests
│ │ ├─ Resources
│ │ │ ├─ ...
│ │ │
│ │ ├─ Sources
│ │ │ ├─ ...
│ │ │
│
│
│
├── Modules
├── Project.swift
│
├── My-Modules-Tuist.xcodeproj
├── My-Modules
...
Requirements:
According to our internal requirements
we want to have separate test targets for UnitTests and IntegrationTests (and UI tests):
- “My-LondonApp” - (Main App target) app target for App Store
- “My-LondonAppUnitTests” - ( product: .unitTests,) target. “My-LondonApp” is host target
- “My-LondonAppIntegrationTests” - ( product: .unitTests,) target. “My-LondonApp” is host target
App Target Definition in Tuist:
So thats why I define target in Tuist’s manifest next:
(targetConfig.name contains “My-LondonApp”)
let result: Target = .target(
name: targetConfig.name,
destinations: My-ProjectConf.destinations,
product: .app,
productName: targetConfig.productName,
bundleId: targetConfig.bundleId,
deploymentTargets: My-ProjectConf.deploymentTargets,
infoPlist: .file(path: "My-Project/App/Resources/Info.plist"),
sources: [
"My-Project/App/Sources/**",
],
resources:
.resources(
[
"My-Project/App/Resources/Sounds/**",
"My-Project/App/Resources/My-Project.sqlite",
"My-Project/App/Sources/**/*.storyboard",
"My-Project/App/Sources/**/*.xib",
...
],
privacyManifest: PrivacyInfo.makePrivacyManifest(type: targetConfig.privacyManifestType)
),
headers: .headers(private: "My-Project/App/Sources/**"),
entitlements: entitlementsFile(from: "My-Project/App/Resources/Entitlements.plist"),
scripts: appTargetScripts(),
dependencies: [
.external(name: "GoogleTagManager"),
// https://firebase.google.com/docs/ios/setup#available-pods
.external(name: "FirebaseCore"),
// local Modules
.project(target: "NetworkKit", path: "../Modules"),
],
settings: appTargetSettings(config: targetConfig)
)
and build schema (what I understand, If I want use tests i need to create custom schema)
let result: Scheme =
.scheme(
name: targetConfig.name,
shared: true,
buildAction: .buildAction(targets: [
.target(targetConfig.name)
]),
testAction: .testPlans([
.relativeToManifest("My-Project/Tests/AllProjectTests.xctestplan")
])
)
return result
the most interesting, targets of tests:
target for UnitTests
let result: Target = .target(
name: "\(hostTargetConfig.name)UnitTests",
destinations: My-ProjectConf.destinations,
product: .unitTests,
bundleId: "\(hostTargetConfig.bundleId)UnitTests",
deploymentTargets: My-ProjectConf.deploymentTargets,
infoPlist: .default,
sources: [
"My-Project/Tests/UnitTests/Sources/**",
],
resources: [
"My-Project/Tests/UnitTests/Resources/**",
],
dependencies: [
.target(name: hostTargetConfig.name),
.xctest,
],
settings: Self.testsTargetSettings()
)
return result
Scheme for UnitTestTarget
let testAction: ProjectDescription.TestAction? = .testPlans([
.relativeToManifest("My-Project/Tests/UnitTests.xctestplan")
])
let result: Scheme =
.scheme(
name: "\(hostTargetConfig.name)UnitTests",
shared: true,
buildAction: .buildAction(targets: [
.target("\(hostTargetConfig.name)UnitTests"),
]),
testAction: testAction,
runAction: .runAction(
configuration: .debug,
executable: .target(hostTargetConfig.name),
arguments: .arguments(
environmentVariables: [
:
],
launchArguments: [
]
)
)
)
target for IntegrationTests
let result: Target = .target(
name: "\(hostTargetConfig.name)IntegrationTests",
destinations: My-ProjectConf.destinations,
product: .unitTests,
bundleId: "\(hostTargetConfig.bundleId)IntegrationTests",
deploymentTargets: My-ProjectConf.deploymentTargets,
infoPlist: .default,
sources: [
"My-Project/Tests/IntegrationTests/Sources/**",
],
resources: [
"My-Project/Tests/IntegrationTests/Resources/**",
],
dependencies: [
.target(name: hostTargetConfig.name),
.xctest,
.external(name: "Mocker"),
],
settings: Self.testsTargetSettings()
)
Scheme for IntegrationTestsTarget
let testAction: ProjectDescription.TestAction? = .testPlans([
.relativeToManifest("My-Project/Tests/IntegrationTests.xctestplan")
])
let result: Scheme =
.scheme(
name: "\(hostTargetConfig.name)IntegrationTests",
shared: true,
buildAction: .buildAction(targets: [
.target("\(hostTargetConfig.name)IntegrationTests"),
]),
testAction: testAction,
runAction: .runAction(
arguments: .arguments(
environmentVariables: [
:
],
launchArguments: [
]
)
)
)
Questions:
-
How can I resolve the error “Scheme “My-LondonApp” is not testable.”?
how to make “My-LondonApp” testable(according to this, target has to start tests from my test plan “AllProjectTests.xctestplan”)? -
What is the correct approach to defining associated test targets
- unitTests - target “My-LondonAppUnitTests”
- integrationTests - target “My-LondonAppIntegrationTests”
- uiTests - target “My-LondonAppUITests”
in Tuist’s manifest to ensure they are correctly related to the main target?