| name | cocoapods-test-specs |
| description | Use when adding automated tests to CocoaPods libraries using test specs. Covers test spec configuration, app host requirements, and testing patterns that integrate with pod lib lint validation. |
| allowed-tools | Read, Write, Edit, Bash, Grep, Glob |
CocoaPods - Test Specs
Integrate automated tests into your CocoaPods library that run during validation.
What Are Test Specs?
Test specs define test targets that CocoaPods builds and runs automatically during pod lib lint and pod spec lint validation.
Benefits
- Automatic Testing: Tests run during every lint validation
- Confidence: Validates library works as expected before publishing
- CI Integration: Consistent testing across all environments
- Documentation: Tests serve as usage examples
Basic Test Spec
Pod::Spec.new do |spec|
spec.name = 'MyLibrary'
spec.version = '1.0.0'
# Main library source
spec.source_files = 'Source/**/*.swift'
# Test spec
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
# Test dependencies
test_spec.dependency 'Quick', '~> 7.0'
test_spec.dependency 'Nimble', '~> 12.0'
end
end
App Host Requirements
Tests Without App Host
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
# Unit tests that don't need app environment
test_spec.requires_app_host = false # Default
end
Tests With App Host
spec.test_spec 'UITests' do |test_spec|
test_spec.source_files = 'Tests/UITests/**/*.swift'
# Tests that need app environment (UIKit, storyboards, etc.)
test_spec.requires_app_host = true
test_spec.dependency 'MyLibrary'
end
Multiple Test Specs
Pod::Spec.new do |spec|
spec.name = 'MyLibrary'
# Unit tests (no app host)
spec.test_spec 'UnitTests' do |unit|
unit.source_files = 'Tests/Unit/**/*.swift'
unit.dependency 'Quick'
unit.dependency 'Nimble'
end
# Integration tests (with app host)
spec.test_spec 'IntegrationTests' do |integration|
integration.source_files = 'Tests/Integration/**/*.swift'
integration.requires_app_host = true
integration.dependency 'MyLibrary'
end
# UI tests (with app host)
spec.test_spec 'UITests' do |ui|
ui.source_files = 'Tests/UI/**/*.swift'
ui.requires_app_host = true
ui.dependency 'MyLibrary'
ui.ios.frameworks = 'XCTest'
end
end
Platform-Specific Test Specs
spec.test_spec 'Tests' do |test_spec|
# Shared test files
test_spec.source_files = 'Tests/Shared/**/*.swift'
# iOS-specific tests
test_spec.ios.source_files = 'Tests/iOS/**/*.swift'
test_spec.ios.frameworks = 'XCTest'
# macOS-specific tests
test_spec.osx.source_files = 'Tests/macOS/**/*.swift'
test_spec.osx.frameworks = 'XCTest'
end
Test Dependencies
Testing Frameworks
spec.test_spec 'Tests' do |test_spec|
# Quick/Nimble (BDD style)
test_spec.dependency 'Quick', '~> 7.0'
test_spec.dependency 'Nimble', '~> 12.0'
# Or traditional XCTest (no additional dependencies)
test_spec.frameworks = 'XCTest'
end
Mocking Frameworks
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
# OCMock for Objective-C
test_spec.dependency 'OCMock', '~> 3.9'
# Cuckoo for Swift
test_spec.dependency 'Cuckoo', '~> 2.0'
# MockingKit for Swift
test_spec.dependency 'MockingKit', '~> 1.0'
end
Test Resources
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
# Test resources (JSON fixtures, images, etc.)
test_spec.resources = 'Tests/Fixtures/**/*'
# Or test resource bundle
test_spec.resource_bundles = {
'MyLibraryTests' => ['Tests/Fixtures/**/*']
}
end
Scheme Configuration
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
# Scheme name (optional - auto-generated if not specified)
test_spec.scheme = { :name => 'MyLibrary-Tests' }
# Code coverage
test_spec.scheme = {
:code_coverage => true
}
end
Testing Subspecs
Pod::Spec.new do |spec|
spec.name = 'MyLibrary'
# Core subspec
spec.subspec 'Core' do |core|
core.source_files = 'Source/Core/**/*.swift'
# Tests for Core subspec
core.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/Core/**/*.swift'
test_spec.dependency 'Quick'
test_spec.dependency 'Nimble'
end
end
# Networking subspec
spec.subspec 'Networking' do |net|
net.source_files = 'Source/Networking/**/*.swift'
net.dependency 'MyLibrary/Core'
# Tests for Networking subspec
net.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/Networking/**/*.swift'
test_spec.dependency 'OHHTTPStubs', '~> 9.0'
end
end
end
Common Testing Patterns
XCTest Pattern
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
test_spec.frameworks = 'XCTest'
# No additional dependencies needed
# Tests use import XCTest
end
Quick/Nimble Pattern
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
test_spec.dependency 'Quick', '~> 7.0'
test_spec.dependency 'Nimble', '~> 12.0'
# Tests use QuickSpec and expect()
end
Network Mocking Pattern
spec.test_spec 'NetworkTests' do |test_spec|
test_spec.source_files = 'Tests/Network/**/*.swift'
# Mock HTTP responses
test_spec.dependency 'OHHTTPStubs', '~> 9.0'
# Or URLProtocol-based mocking
test_spec.dependency 'Mocker', '~> 3.0'
end
Snapshot Testing Pattern
spec.test_spec 'SnapshotTests' do |test_spec|
test_spec.source_files = 'Tests/Snapshots/**/*.swift'
test_spec.requires_app_host = true
# Snapshot testing framework
test_spec.dependency 'SnapshotTesting', '~> 1.15'
# Reference images
test_spec.resources = 'Tests/Snapshots/__Snapshots__/**/*'
end
Running Tests
During Lint Validation
# Tests run automatically
pod lib lint
# Skip tests (faster, but not recommended)
pod lib lint --skip-tests
# Verbose test output
pod lib lint --verbose
Standalone Test Execution
# In Example app
cd Example
pod install
xcodebuild test -workspace MyLibrary.xcworkspace -scheme MyLibrary-Tests
Best Practices
Directory Structure
MyLibrary/
├── MyLibrary.podspec
├── Source/
│ └── MyLibrary/
├── Tests/
│ ├── Unit/ # Unit tests
│ ├── Integration/ # Integration tests
│ ├── UI/ # UI tests
│ └── Fixtures/ # Test data
└── Example/
└── MyLibraryExample.xcodeproj
Test Organization
# Organize by test type
spec.test_spec 'UnitTests' do |unit|
unit.source_files = 'Tests/Unit/**/*.swift'
unit.dependency 'Quick'
unit.dependency 'Nimble'
end
spec.test_spec 'IntegrationTests' do |integration|
integration.source_files = 'Tests/Integration/**/*.swift'
integration.requires_app_host = true
end
Dependency Management
spec.test_spec 'Tests' do |test_spec|
# Only test dependencies here
test_spec.dependency 'Quick'
test_spec.dependency 'Nimble'
# Main library dependencies go in main spec
# Not in test spec
end
Anti-Patterns
Don't
❌ Skip tests during validation
pod lib lint --skip-tests # Defeats purpose of test specs
❌ Mix test and production code
spec.source_files = 'Source/**/*.swift', 'Tests/**/*.swift' # BAD
❌ Include test dependencies in main spec
# In main spec
spec.dependency 'Quick' # Should be in test_spec only
❌ Use requires_app_host unnecessarily
spec.test_spec 'Tests' do |test_spec|
# Pure unit tests don't need app host
test_spec.requires_app_host = true # Slower, unnecessary
end
Do
✅ Run tests during every validation
pod lib lint # Includes tests by default
✅ Separate test and production code
spec.source_files = 'Source/**/*.swift'
spec.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*.swift'
end
✅ Keep test dependencies in test spec
spec.test_spec 'Tests' do |test_spec|
test_spec.dependency 'Quick' # Only for tests
end
✅ Use app host only when needed
spec.test_spec 'Tests' do |test_spec|
# Only if tests need UIKit, storyboards, etc.
test_spec.requires_app_host = true
end
Example: Complete Test Spec Setup
Pod::Spec.new do |spec|
spec.name = 'MyAwesomeLibrary'
spec.version = '1.0.0'
spec.summary = 'An awesome library'
spec.homepage = 'https://github.com/username/MyAwesomeLibrary'
spec.license = { :type => 'MIT', :file => 'LICENSE' }
spec.authors = { 'Your Name' => 'email@example.com' }
spec.source = { :git => 'https://github.com/username/MyAwesomeLibrary.git', :tag => spec.version.to_s }
spec.ios.deployment_target = '13.0'
spec.swift_versions = ['5.7', '5.8', '5.9']
spec.source_files = 'Source/**/*.swift'
# Unit tests
spec.test_spec 'UnitTests' do |unit|
unit.source_files = 'Tests/Unit/**/*.swift'
unit.dependency 'Quick', '~> 7.0'
unit.dependency 'Nimble', '~> 12.0'
end
# Integration tests with app host
spec.test_spec 'IntegrationTests' do |integration|
integration.source_files = 'Tests/Integration/**/*.swift'
integration.requires_app_host = true
integration.dependency 'OHHTTPStubs', '~> 9.0'
end
end
Related Skills
- cocoapods-podspec-fundamentals
- cocoapods-subspecs-organization
- cocoapods-publishing-workflow