Skip to main content

test-spec

Generates comprehensive test specification with unit tests, UI tests, accessibility testing, and beta testing plan. Creates TEST_SPEC.md from PRD and implementation specs. Use when creating QA strategy.

Source facts

Repository
rshankras/claude-code-apple-skills
Last source activity
July 16, 2026 at 12:36
Detected SKILL.md language
English
Stars
770
Forks
74

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
test-spec
description
Generates comprehensive test specification with unit tests, UI tests, accessibility testing, and beta testing plan. Creates TEST_SPEC.md from PRD and implementation specs. Use when creating QA strategy.
allowed-tools
["Read","Write","Glob","Grep","AskUserQuestion"]
last_verified
2026-07-16T00:00:00.000Z
review_by
2027-06-22T00:00:00.000Z
os_version
iOS 27 / macOS 27
# Test Specification Skill Generate comprehensive test specification and QA plan for iOS/macOS app testing. ## Metadata - **Name**: test-spec - **Version**: 1.0.0 - **Role**: QA Engineer - **Author**: ProductAgent Team ## When This Skill Activates This skill activates when the user says: - "generate test spec" - "create QA plan" - "write testing guide" - "generate test cases" - "create test specification" ## Description You are a QA Engineer AI agent specializing in iOS/macOS app testing. Your job is to transform product requirements and implementation details into a comprehensive test specification that ensures quality, identifies edge cases, and provides clear test cases for both automated and manual testing. ## Prerequisites Before activating this skill, ensure: 1. PRD exists (from prd-generator skill) with features and acceptance criteria 2. IMPLEMENTATION_GUIDE exists (from implementation-guide skill) with code structure 3. UX_SPEC exists (from ux-spec skill) for UI testing scenarios ## Input Sources Read and extract information from: 1. **docs/PRD.md** - All features with acceptance criteria - User stories (Given/When/Then format) - Success criteria - Non-functional requirements 2. **docs/IMPLEMENTATION_GUIDE.md** - All ViewModels to test - All data models to test - API endpoints to test - File structure for organizing tests 3. **docs/UX_SPEC.md** - All user flows - All interactions - All states (empty, loading, error) - Edge cases documented 4. **docs/ARCHITECTURE.md** - Testing strategy overview - Tech stack (for choosing testing tools) ## Output Generate: **docs/TEST_SPEC.md** Structure: ```markdown # Test Specification: [App Name] **Version**: 1.0.0 **Last Updated**: [Date] **Status**: Draft / In Review / Approved **QA Engineer**: QA Engineer AI **Platform**: iOS [Version]+ --- ## 1. Test Strategy ### 1.1 Test Pyramid Our testing approach follows the test pyramid: ``` /\\ / \\ UI Tests (10%) / \\ Critical user journeys, happy paths /------\\ / \\ Integration Tests (20%) / \\ API integration, data persistence, service layer /------------\\ / \\ Unit Tests (70%) / \\ ViewModels, Models, Utilities, Business Logic /------------------\\ ``` **Rationale**: - **Unit Tests (70%)**: Fast, reliable, easy to maintain. Focus on business logic. - **Integration Tests (20%)**: Test component interactions (API + Database, ViewModel + Service). - **UI Tests (10%)**: Slow and brittle, only for critical user flows. ### 1.2 Testing Levels **Level 1: Unit Testing** - **Scope**: Individual functions, methods, ViewModels, Models - **Tools**: XCTest - **Run Frequency**: On every commit (CI/CD) - **Target Coverage**: 80%+ code coverage **Level 2: Integration Testing** - **Scope**: Multiple components working together - **Tools**: XCTest with mock/stub services - **Run Frequency**: On every PR merge - **Target Coverage**: All critical data flows **Level 3: UI Testing** - **Scope**: End-to-end user journeys - **Tools**: XCUITest - **Run Frequency**: Before release - **Target Coverage**: All P0 user flows **Level 4: Manual Testing** - **Scope**: Exploratory testing, edge cases, UX validation - **Tools**: TestFlight beta - **Run Frequency**: Before each release - **Target Coverage**: Full app walkthrough ### 1.3 Test Environments **Development**: - Local Xcode testing - In-memory database (SwiftData) - Mock API responses - Fast feedback loop **Staging**: - TestFlight internal testing - Staging API environment - Real backend integration - Pre-production validation **Production**: - Phased rollout (10% → 50% → 100%) - Real user monitoring - Crash analytics - Performance monitoring ### 1.4 Testing Tools | Tool | Purpose | When to Use | |------|---------|-------------| | XCTest | Unit & integration tests | Always | | XCUITest | UI automation tests | Critical flows | | TestFlight | Beta testing | Pre-release | | Xcode Instruments | Performance profiling | Optimization phase | | Accessibility Inspector | Accessibility audit | Every release | | Network Link Conditioner | Network testing | Edge case testing | --- ## 2. Unit Test Cases ### 2.1 Data Model Tests Test all `@Model` classes from ARCHITECTURE.md. #### Test Suite: User Model **File**: `[AppName]Tests/ModelTests/UserTests.swift` | Test Case | Setup | Input | Expected Output | Priority | |-----------|-------|-------|-----------------|----------| | testUserInitialization | None | name: "John Doe", email: "john@test.com" | User object created with UUID, timestamps set | P0 | | testUserInitializationWithEmptyName | None | name: "", email: "test@test.com" | User created but isValid returns false | P1 | | testEmailValidation_Valid | User instance | email: "valid@example.com" | isValid returns true | P0 | | testEmailValidation_Invalid | User instance | email: "invalid.com" | isValid returns false | P0 | | testEmailValidation_Empty | User instance | email: "" | isValid returns false | P0 | | testDisplayName_SingleName | User with name: "John" | Call displayName | Returns "John" | P1 | | testDisplayName_FullName | User with name: "John Doe" | Call displayName | Returns "John" | P1 | | testInitials_SingleName | User with name: "John" | Call initials | Returns "J" | P2 | | testInitials_FullName | User with name: "John Doe" | Call initials | Returns "JD" | P2 | | testUpdateProfile_Name | User instance | updateProfile(name: "Jane") | name updated, updatedAt changed | P1 | | testUpdateProfile_Email | User instance | updateProfile(email: "new@test.com") | email updated, updatedAt changed | P1 | | testCodable_Encoding | User instance | Encode to JSON | Valid JSON with snake_case keys | P1 | | testCodable_Decoding | JSON data | Decode from JSON | User object created correctly | P1 | **Implementation Example**: ```swift import XCTest @testable import [AppName] final class UserTests: XCTestCase { var sut: User! // System Under Test override func setUp() { super.setUp() // Setup runs before each test sut = User(name: "Test User", email: "test@example.com") } override func tearDown() { // Cleanup runs after each test sut = nil super.tearDown() } // Test: User initialization creates valid object func testUserInitialization() { // Given: Setup in setUp() // When: User is initialized (done in setUp) // Then: Verify properties XCTAssertNotNil(sut.id, "ID should be generated") XCTAssertEqual(sut.name, "Test User") XCTAssertEqual(sut.email, "test@example.com") XCTAssertNotNil(sut.createdAt) XCTAssertNotNil(sut.updatedAt) XCTAssertTrue(sut.items.isEmpty, "New user should have no items") } // Test: Valid email passes validation func testEmailValidation_Valid() { // Given sut.email = "valid@example.com" // When let isValid = sut.isValid // Then XCTAssertTrue(isValid, "Valid email should pass validation") } // Test: Invalid email fails validation func testEmailValidation_Invalid() { // Given sut.email = "invalid.com" // Missing @ // When let isValid = sut.isValid // Then XCTAssertFalse(isValid, "Invalid email should fail validation") } // Test: Display name returns first name only func testDisplayName_FullName() { // Given sut.name = "John Doe" // When let displayName = sut.displayName // Then XCTAssertEqual(displayName, "John", "Display name should be first name only") } // Test: Initials are correctly generated func testInitials_FullName() { // Given sut.name = "John Doe" // When let initials = sut.initials // Then XCTAssertEqual(initials, "JD", "Initials should be JD") } // Test: Update profile changes name and timestamp func testUpdateProfile_Name() { // Given let originalUpdatedAt = sut.updatedAt sleep(1) // Ensure timestamp changes // When sut.updateProfile(name: "Jane Doe") // Then XCTAssertEqual(sut.name, "Jane Doe") XCTAssertNotEqual(sut.updatedAt, originalUpdatedAt, "updatedAt should change") } // Test: Codable encoding produces valid JSON func testCodable_Encoding() throws { // Given: User from setUp // When let encoder = JSONEncoder() let data = try encoder.encode(sut) let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] // Then XCTAssertNotNil(json) XCTAssertEqual(json?["name"] as? String, "Test User") XCTAssertEqual(json?["email"] as? String, "test@example.com") XCTAssertNotNil(json?["id"]) } } ``` **Repeat for all models**: Item, Category, etc. --- #### Test Suite: Item Model [Similar structure for ItemTests.swift] **Key Test Cases**: - Initialization - Validation (title not empty, description not empty) - Status updates - Relationships (owner, category) - Codable conformance --- ### 2.2 ViewModel Tests Test all ViewModels from IMPLEMENTATION_GUIDE. #### Test Suite: HomeViewModel **File**: `[AppName]Tests/ViewModelTests/HomeViewModelTests.swift` | Test Case | Mock Setup | Action | Expected Result | Priority | |-----------|------------|--------|-----------------|----------| | testInitialState | New ViewModel | None | items empty, isLoading false, showError false | P0 | | testLoadItems_Success | Mock API returns 3 items | Call loadItems() | isLoading becomes false, items count = 3, no error | P0 | | testLoadItems_Failure | Mock API throws error | Call loadItems() | isLoading false, showError true, errorMessage set | P0 | | testLoadItems_EmptyResponse | Mock API returns empty array | Call loadItems() | items empty, no error | P1 | | testRefresh | 3 items already loaded | Call refresh() | isLoading true then false, items refreshed | P1 | | testDeleteItem_Success | 3 items loaded | Delete item[1] | items count = 2, deleted item removed | P0 | | testDeleteItem_Failure | Mock API throws error on delete | Delete item | showError true, item not removed | P1 | | testToggleItemStatus_Active | Item with status .active | toggleItemStatus(item) | item.status becomes .completed | P0 | | testToggleItemStatus_Completed | Item with status .completed | toggleItemStatus(item) | item.status becomes .active | P0 | | testSearchItems_MatchFound | 3 items, searchQuery = "Test" | Set searchQuery | items filtered to matching only | P1 | | testSearchItems_NoMatch | 3 items, searchQuery = "XYZ" | Set searchQuery | items empty | P1 | | testSearchItems_EmptyQuery | searchQuery was "Test" | Set searchQuery = "" | All items shown again | P1 | | testLoadItems_LocalFirst | Mock delays API response | Call loadItems() | Local items shown immediately, then synced | P2 | **Implementation Example**: ```swift import XCTest @testable import [AppName] final class HomeViewModelTests: XCTestCase { var sut: HomeViewModel! var mockAPIClient: MockAPIClient! var mockDataManager: MockDataManager! override func setUp() { super.setUp() mockAPIClient = MockAPIClient() mockDataManager = MockDataManager() sut = HomeViewModel( apiClient: mockAPIClient, dataManager: mockDataManager ) } override func tearDown() { sut = nil mockAPIClient = nil mockDataManager = nil super.tearDown() } // Test: Initial state is correct @MainActor func testInitialState() { // Then XCTAssertTrue(sut.items.isEmpty) XCTAssertFalse(sut.isLoading) XCTAssertFalse(sut.showError) XCTAssertTrue(sut.searchQuery.isEmpty) } // Test: Loading items succeeds @MainActor func testLoadItems_Success() async { // Given let mockItems = [ Item(title: "Item 1", description: "Desc 1", owner: mockUser()), Item(title: "Item 2", description: "Desc 2", owner: mockUser()), Item(title: "Item 3", description: "Desc 3", owner: mockUser()) ] mockDataManager.itemsToReturn = mockItems mockAPIClient.itemsToReturn = mockItems // When await sut.loadItems() // Then XCTAssertEqual(sut.items.count, 3) XCTAssertFalse(sut.isLoading) XCTAssertFalse(sut.showError) } // Test: Loading items handles error @MainActor func testLoadItems_Failure() async { // Given mockDataManager.shouldThrowError = true // When await sut.loadItems() // Then XCTAssertTrue(sut.showError) XCTAssertFalse(sut.errorMessage.isEmpty) XCTAssertFalse(sut.isLoading) }
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub