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

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.6 KB
---
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

Overview

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.

How this skill works

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).

When to use it

  • When you want tests to run automatically during pod lib lint or pod spec lint
  • When adding unit, integration, or UI tests to a CocoaPods library
  • When tests require an app environment (storyboards, UIKit) and need requires_app_host
  • When you need platform-specific test behavior or resource bundles for tests
  • When you want CI to validate library behavior before publishing

Best practices

  • Keep production and test source files separated; use spec.source_files for library and spec.test_spec for tests
  • Declare test dependencies inside test_spec only; avoid adding test frameworks to the main spec
  • Use requires_app_host = true only for tests that require an app environment to avoid slower validation
  • Organize test folders by type (Unit, Integration, UI) and platform for clarity
  • Include resources or resource_bundles in the test_spec for fixtures, snapshots, or test assets

Example use cases

  • Unit tests for pure logic using XCTest or Quick/Nimble that run during lint validation
  • Integration tests that require an app host to exercise UIKit or storyboards
  • Network tests using OHHTTPStubs or Mocker to simulate HTTP responses during CI validation
  • Snapshot tests that require resources and requires_app_host to render views and compare images
  • Subspec-level test_spec blocks to test isolated subsystems like Core or Networking

FAQ

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.