home / skills / thebushidocollective / han / test-specs
This skill helps you configure and run automated CocoaPods test specs for reliable lint validation across subspecs and platforms.
npx playbooks add skill thebushidocollective/han --skill test-specsReview the files below or copy the command above to add this skill to your agents.
---
name: cocoapods-test-specs
user-invocable: false
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```ruby
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
```bash
# 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
```bash
# 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
```ruby
# 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
```ruby
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
```bash
pod lib lint --skip-tests # Defeats purpose of test specs
```
❌ Mix test and production code
```ruby
spec.source_files = 'Source/**/*.swift', 'Tests/**/*.swift' # BAD
```
❌ Include test dependencies in main spec
```ruby
# In main spec
spec.dependency 'Quick' # Should be in test_spec only
```
❌ Use requires_app_host unnecessarily
```ruby
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
```bash
pod lib lint # Includes tests by default
```
✅ Separate test and production code
```ruby
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
```ruby
spec.test_spec 'Tests' do |test_spec|
test_spec.dependency 'Quick' # Only for tests
end
```
✅ Use app host only when needed
```ruby
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
```ruby
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 protected]' }
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
This skill helps CocoaPods authors integrate automated tests into pod specs using test_spec blocks. It explains test spec configuration, app host requirements, platform-specific setup, and patterns that run during pod lib lint validation. The goal is reliable, CI-friendly test execution without mixing test and production code.
The skill inspects how to declare spec.test_spec entries inside a Pod::Spec and how to attach test source files, dependencies, resources, and scheme settings. It explains requires_app_host for tests needing a host app, platform overrides (ios/osx), and how tests run automatically during pod lib lint and pod spec lint. It also outlines best practices for organizing tests, managing test-only dependencies, and common testing patterns (XCTest, Quick/Nimble, mocking, snapshots).
Do test specs run automatically during pod lib lint?
Yes. pod lib lint and pod spec lint run test specs by default unless you pass --skip-tests.
Where should I put test-only dependencies?
Declare them inside spec.test_spec blocks. Do not add test frameworks to the main spec dependencies.
When should I set requires_app_host = true?
Only for tests that need an app process or UIKit features, such as UI or snapshot tests; unit tests should keep it false.