🎯

swift-testing

🎯Skill

from pluginagentmarketplace/custom-plugin-swift

VibeIndex|
What it does

Enables comprehensive Swift testing across frameworks, covering unit, integration, UI, and snapshot tests with advanced mocking and coverage tracking.

πŸ“¦

Part of

pluginagentmarketplace/custom-plugin-swift(12 items)

swift-testing

Installation

Add MarketplaceAdd marketplace to Claude Code
/plugin marketplace add pluginagentmarketplace/custom-plugin-swift
Install PluginInstall plugin from marketplace
/plugin install swift-development-assistant
git cloneClone repository
git clone https://github.com/pluginagentmarketplace/custom-plugin-swift.git
Claude CodeAdd plugin in Claude Code
/plugin load .
πŸ“– Extracted from docs: pluginagentmarketplace/custom-plugin-swift
5Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

Test Swift applications - XCTest, Swift Testing, UI tests, mocking, TDD, CI/CD

Overview

# Swift Testing Skill

Comprehensive testing strategies for Swift applications using XCTest and Swift Testing framework.

Prerequisites

  • Xcode 15+ installed
  • Understanding of dependency injection
  • Familiarity with async/await

Parameters

```yaml

parameters:

framework:

type: string

enum: [xctest, swift_testing]

default: swift_testing

test_type:

type: string

enum: [unit, integration, ui, snapshot]

default: unit

coverage_target:

type: number

default: 80

description: Target code coverage percentage

ci_platform:

type: string

enum: [xcode_cloud, github_actions, gitlab_ci, none]

default: github_actions

```

Topics Covered

Test Frameworks

| Framework | Min Version | Key Features |

|-----------|-------------|--------------|

| XCTest | iOS 2.0+ | XCTestCase, expectations |

| Swift Testing | iOS 17+ / Swift 5.9+ | @Test, #expect, traits |

Test Types

| Type | Scope | Speed |

|------|-------|-------|

| Unit | Single function/class | Fastest |

| Integration | Multiple components | Medium |

| UI | Full user flows | Slowest |

| Snapshot | Visual regression | Medium |

Testing Patterns

| Pattern | Purpose |

|---------|---------|

| AAA | Arrange, Act, Assert |

| Given-When-Then | BDD style |

| Test Doubles | Mock, Stub, Spy, Fake |

Code Examples

Swift Testing (iOS 17+ / Swift 5.9+)

```swift

import Testing

@testable import MyApp

@Suite("ShoppingCart Tests")

struct ShoppingCartTests {

var cart: ShoppingCart

var mockRepository: MockProductRepository

init() {

mockRepository = MockProductRepository()

cart = ShoppingCart(repository: mockRepository)

}

@Test("adding product increases count")

func addProduct() async throws {

let product = Product(id: "1", name: "Widget", price: 9.99)

cart.add(product)

#expect(cart.items.count == 1)

#expect(cart.items.first?.product == product)

}

@Test("adding same product increases quantity")

func addSameProductTwice() {

let product = Product(id: "1", name: "Widget", price: 9.99)

cart.add(product)

cart.add(product)

#expect(cart.items.count == 1)

#expect(cart.items.first?.quantity == 2)

}

@Test("total calculates correctly")

func calculateTotal() {

cart.add(Product(id: "1", name: "A", price: 10.00))

cart.add(Product(id: "2", name: "B", price: 20.00))

#expect(cart.total == 30.00)

}

@Test("checkout requires non-empty cart", .tags(.checkout))

func checkoutEmptyCart() async {

await #expect(throws: CartError.empty) {

try await cart.checkout()

}

}

@Test("checkout with valid cart", .tags(.checkout))

func checkoutSuccess() async throws {

cart.add(Product(id: "1", name: "Widget", price: 9.99))

mockRepository.checkoutResult = .success(Order(id: "order-1"))

let order = try await cart.checkout()

#expect(order.id == "order-1")

#expect(cart.items.isEmpty)

}

@Test(arguments: [0, 1, 5, 10])

func discountTiers(quantity: Int) {

let discount = cart.calculateDiscount(forQuantity: quantity)

switch quantity {

case 0..<5: #expect(discount == 0)

case 5..<10: #expect(discount == 0.05)

default: #expect(discount == 0.10)

}

}

}

```

XCTest with Async

```swift

import XCTest

@testable import MyApp

final class ProductServiceTests: XCTestCase {

var sut: ProductService!

var mockAPI: MockAPIClient!

override func setUp() {

super.setUp()

mockAPI = MockAPIClient()

sut = ProductService(api: mockAPI)

}

override func tearDown() {

sut = nil

mockAPI = nil

super.tearDown()

}

func test_fetchProducts_success() async throws {

// Arrange

let expectedProducts = [Product(id: "1", name: "Test", price: 9.99)]

mockAPI.productsResult = .success(expectedProducts)

// Act

let products = try await sut.fetchProducts()

// Assert

XCTAssertEqual(products, expectedProducts)

XCTAssertTrue(mockAPI.fetchProductsCalled)

}

func test_fetchProducts_networkError_throws() async {

// Arrange

mockAPI.productsResult = .failure(NetworkError.noConnection)

// Act & Assert

do {

_ = try await sut.fetchProducts()

XCTFail("Expected error to be thrown")

} catch {

XCTAssertTrue(error is NetworkError)

}

}

func test_fetchProducts_retries_onTransientError() async throws {

// Arrange

var attempts = 0

mockAPI.onFetchProducts = {

attempts += 1

if attempts < 3 {

throw NetworkError.timeout

}

return [Product(id: "1", name: "Test", price: 9.99)]

}

// Act

_ = try await sut.fetchProductsWithRetry(maxAttempts: 3)

// Assert

XCTAssertEqual(attempts, 3)

}

}

```

Mock Implementation

```swift

// Protocol for abstraction

protocol APIClientProtocol {

func fetchProducts() async throws -> [Product]

func createOrder(_ order: CreateOrderRequest) async throws -> Order

}

// Production implementation

final class APIClient: APIClientProtocol {

func fetchProducts() async throws -> [Product] {

// Real implementation

}

func createOrder(_ order: CreateOrderRequest) async throws -> Order {

// Real implementation

}

}

// Test mock

final class MockAPIClient: APIClientProtocol {

var productsResult: Result<[Product], Error> = .success([])

var orderResult: Result = .success(Order(id: "mock"))

var fetchProductsCalled = false

var fetchProductsCallCount = 0

var createOrderCalled = false

var lastOrderRequest: CreateOrderRequest?

var onFetchProducts: (() async throws -> [Product])?

func fetchProducts() async throws -> [Product] {

fetchProductsCalled = true

fetchProductsCallCount += 1

if let handler = onFetchProducts {

return try await handler()

}

return try productsResult.get()

}

func createOrder(_ order: CreateOrderRequest) async throws -> Order {

createOrderCalled = true

lastOrderRequest = order

return try orderResult.get()

}

func reset() {

productsResult = .success([])

orderResult = .success(Order(id: "mock"))

fetchProductsCalled = false

fetchProductsCallCount = 0

createOrderCalled = false

lastOrderRequest = nil

onFetchProducts = nil

}

}

```

UI Testing with Page Object Pattern

```swift

import XCTest

// Page Object

struct LoginPage {

let app: XCUIApplication

var usernameField: XCUIElement {

app.textFields["username"]

}

var passwordField: XCUIElement {

app.secureTextFields["password"]

}

var loginButton: XCUIElement {

app.buttons["login"]

}

var errorMessage: XCUIElement {

app.staticTexts["errorMessage"]

}

func login(username: String, password: String) {

usernameField.tap()

usernameField.typeText(username)

passwordField.tap()

passwordField.typeText(password)

loginButton.tap()

}

func waitForLogin(timeout: TimeInterval = 5) -> Bool {

!usernameField.waitForExistence(timeout: timeout)

}

}

// UI Test

final class LoginUITests: XCTestCase {

var app: XCUIApplication!

var loginPage: LoginPage!

override func setUp() {

super.setUp()

continueAfterFailure = false

app = XCUIApplication()

app.launchArguments = ["--uitesting", "--reset-state"]

app.launch()

loginPage = LoginPage(app: app)

}

func test_login_withValidCredentials_navigatesToHome() {

loginPage.login(username: "testuser", password: "password123")

XCTAssertTrue(loginPage.waitForLogin())

XCTAssertTrue(app.tabBars["mainTabBar"].exists)

}

func test_login_withInvalidCredentials_showsError() {

loginPage.login(username: "wrong", password: "wrong")

XCTAssertTrue(loginPage.errorMessage.waitForExistence(timeout: 5))

XCTAssertEqual(loginPage.errorMessage.label, "Invalid credentials")

}

}

```

GitHub Actions CI

```yaml

name: Tests

on:

push:

branches: [main, develop]

pull_request:

branches: [main]

jobs:

test:

runs-on: macos-14

steps:

- uses: actions/checkout@v4

- name: Select Xcode

run: sudo xcode-select -s /Applications/Xcode_15.2.app

- name: Build and Test

run: |

xcodebuild test \

-scheme MyApp \

-destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' \

-resultBundlePath TestResults.xcresult \

-enableCodeCoverage YES \

CODE_SIGNING_ALLOWED=NO

- name: Upload Results

uses: actions/upload-artifact@v3

if: failure()

with:

name: test-results

path: TestResults.xcresult

- name: Coverage Report

run: |

xcrun xccov view --report TestResults.xcresult

```

Troubleshooting

Common Issues

| Issue | Cause | Solution |

|-------|-------|----------|

| Flaky tests | Shared state | Add setUp/tearDown cleanup |

| Async timeout | Missing fulfillment | Call fulfill() or increase timeout |

| UI element not found | Wrong identifier | Check accessibilityIdentifier |

| Mock not working | Wrong initialization | Verify dependency injection |

| Coverage low | Untested paths | Add edge case tests |

Debug Tips

```swift

// Print XCUIElement hierarchy

print(app.debugDescription)

// Wait for condition

let exists = element.waitForExistence(timeout: 5)

// Take screenshot on failure

let screenshot = XCUIScreen.main.screenshot()

let attachment = XCTAttachment(screenshot: screenshot)

attachment.lifetime = .keepAlways

add(attachment)

```

Validation Rules

```yaml

validation:

- rule: test_naming

severity: info

check: Use descriptive test names (test_method_condition_result)

- rule: one_assertion

severity: info

check: Prefer one logical assertion per test

- rule: no_test_interdependence

severity: error

check: Tests must not depend on each other

```

Usage

```

Skill("swift-testing")

```

Related Skills

  • swift-fundamentals - Code to test
  • swift-concurrency - Testing async code
  • swift-architecture - Testable architecture