Documentation/Tools/Cocoapods/ skills /cocoapods-test-specs

šŸ“– cocoapods-test-specs

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


Overview

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@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