[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6KCMEC4YP1FFAX4ZX80Q10R":3,"skill-same:01M6KCMEC4YP1FFAX4ZX80Q10R":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":43,"linkOnly":44,"markdownContent":45,"markdownBody":46,"frontMatter":47,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":6,"ownerHtmlUrl":55,"ownerBlogUrl":56,"ownerDescription":57,"updatedAt":58,"install":59,"resources":72},"01M6KCMEC4YP1FFAX4ZX80Q10R","mobiai-ios-testing",null,"Use when writing or running tests in an iOS project — unit tests, UI tests, snapshot tests, choosing the right framework.","ArisGuimera\u002FMobiAI-Core","ArisGuimera","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F16361358?v=4","01M6KC5MVGRKDA14DWT98445V9","AristiDevs","01M6KC5MZKRFFHFFD340B21YN3",492,"MIT","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fios\u002Fskills\u002Fmobiai-ios-testing\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42],"ios","mobiai ios testing","ios testing","testing","copilot","code","framework","running tests","ui","unit tests","cursor","snapshot","use","claude-code","project","tests","unit","codex","writing","2026-11-10 10:12:30",false,"---\nname: mobiai-ios-testing\ndescription: Use when writing or running tests in an iOS project — unit tests, UI tests, snapshot tests, choosing the right framework.\nlicense: MIT\ncompatibility: [claude-code, cursor, copilot, codex]\nplatforms: [ios]\n---\n\n# iOS Testing\n\n> **Community contribution welcome!** Help improve this skill with your XCTest patterns, mocking strategies, and testing tips.\n\nGuide to writing and running tests in iOS projects.\n\n## When to Use\n\n- Writing unit tests for Swift code\n- Setting up test infrastructure\n- Running and debugging test suites\n\n## Test Types\n\n| Type | Target | Framework | Speed |\n|------|--------|-----------|-------|\n| Unit | `MyAppTests` | XCTest | Fast |\n| UI | `MyAppUITests` | XCUITest | Slow |\n| Snapshot | `MyAppTests` | SnapshotTesting (pointfree) | Medium |\n\n## Unit Testing (XCTest)\n\n### File Location\n\n- Source: `MyApp\u002FFeature\u002FMyViewModel.swift`\n- Test: `MyAppTests\u002FFeature\u002FMyViewModelTests.swift`\n\n### Test Structure\n\n```swift\nimport XCTest\n@testable import MyApp\n\nfinal class MyViewModelTests: XCTestCase {\n\n    private var sut: MyViewModel!\n    private var mockRepository: MockRepository!\n\n    override func setUp() {\n        super.setUp()\n        mockRepository = MockRepository()\n        sut = MyViewModel(repository: mockRepository)\n    }\n\n    override func tearDown() {\n        sut = nil\n        mockRepository = nil\n        super.tearDown()\n    }\n\n    func testLoadData_setsStateToSuccess_whenRepositoryReturnsData() async {\n        \u002F\u002F Given\n        mockRepository.stubbedResult = [Item(id: \"1\", name: \"Test\")]\n\n        \u002F\u002F When\n        await sut.loadData()\n\n        \u002F\u002F Then\n        XCTAssertEqual(sut.state, .success([Item(id: \"1\", name: \"Test\")]))\n    }\n\n    func testLoadData_setsStateToError_whenRepositoryThrows() async {\n        \u002F\u002F Given\n        mockRepository.stubbedError = NSError(domain: \"\", code: -1)\n\n        \u002F\u002F When\n        await sut.loadData()\n\n        \u002F\u002F Then\n        if case .error = sut.state {\n            \u002F\u002F Expected\n        } else {\n            XCTFail(\"Expected error state\")\n        }\n    }\n}\n```\n\n### Naming Conventions\n\n```swift\n\u002F\u002F Pattern: testMethodName_expectedBehavior_whenCondition\nfunc testCalculateTotal_returnsZero_whenCartIsEmpty() { ... }\nfunc testFormatPrice_throws_whenAmountIsNegative() { ... }\n```\n\n### Async Testing\n\n```swift\n\u002F\u002F async\u002Fawait\nfunc testFetchData_returnsItems() async throws {\n    let items = try await sut.fetchData()\n    XCTAssertFalse(items.isEmpty)\n}\n\n\u002F\u002F Combine\nfunc testPublisher_emitsValue() {\n    let expectation = expectation(description: \"Value emitted\")\n    var received: String?\n\n    sut.publisher\n        .sink { value in\n            received = value\n            expectation.fulfill()\n        }\n        .store(in: &cancellables)\n\n    sut.trigger()\n    wait(for: [expectation], timeout: 1.0)\n    XCTAssertEqual(received, \"expected\")\n}\n```\n\n### Manual Mocking (No Framework)\n\n```swift\nclass MockRepository: RepositoryProtocol {\n    var stubbedResult: [Item] = []\n    var stubbedError: Error?\n    var fetchDataCallCount = 0\n\n    func fetchData() async throws -> [Item] {\n        fetchDataCallCount += 1\n        if let error = stubbedError { throw error }\n        return stubbedResult\n    }\n}\n```\n\n## XCUITest (UI Testing)\n\n```swift\nfinal class MyAppUITests: XCTestCase {\n    let app = XCUIApplication()\n\n    override func setUp() {\n        super.setUp()\n        continueAfterFailure = false\n        app.launch()\n    }\n\n    func testLogin_navigatesToHome() {\n        app.textFields[\"Email\"].tap()\n        app.textFields[\"Email\"].typeText(\"test@test.com\")\n        app.secureTextFields[\"Password\"].tap()\n        app.secureTextFields[\"Password\"].typeText(\"password\")\n        app.buttons[\"Login\"].tap()\n\n        XCTAssertTrue(app.staticTexts[\"Welcome\"].waitForExistence(timeout: 5))\n    }\n}\n```\n\n## Running Tests\n\n```bash\n# All unit tests\nxcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15 Pro' -quiet\n\n# Specific test class\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppTests\u002FMyViewModelTests\n\n# Specific test method\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppTests\u002FMyViewModelTests\u002FtestLoadData_setsStateToSuccess\n\n# UI tests\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppUITests\n```\n\n## Quick\u002FNimble (BDD Style)\n\n```swift\nimport Quick\nimport Nimble\n@testable import MyApp\n\nclass MyViewModelSpec: QuickSpec {\n    override class func spec() {\n        describe(\"MyViewModel\") {\n            var sut: MyViewModel!\n\n            beforeEach { sut = MyViewModel() }\n\n            context(\"when data loads successfully\") {\n                it(\"sets state to success\") {\n                    expect(sut.state).toEventually(equal(.success))\n                }\n            }\n        }\n    }\n}\n```\n\n---\n\n**Want to improve this skill?** Add your testing patterns, mocking strategies, and CI tips via a PR.\n","# iOS Testing\n\n> **Community contribution welcome!** Help improve this skill with your XCTest patterns, mocking strategies, and testing tips.\n\nGuide to writing and running tests in iOS projects.\n\n## When to Use\n\n- Writing unit tests for Swift code\n- Setting up test infrastructure\n- Running and debugging test suites\n\n## Test Types\n\n| Type | Target | Framework | Speed |\n|------|--------|-----------|-------|\n| Unit | `MyAppTests` | XCTest | Fast |\n| UI | `MyAppUITests` | XCUITest | Slow |\n| Snapshot | `MyAppTests` | SnapshotTesting (pointfree) | Medium |\n\n## Unit Testing (XCTest)\n\n### File Location\n\n- Source: `MyApp\u002FFeature\u002FMyViewModel.swift`\n- Test: `MyAppTests\u002FFeature\u002FMyViewModelTests.swift`\n\n### Test Structure\n\n```swift\nimport XCTest\n@testable import MyApp\n\nfinal class MyViewModelTests: XCTestCase {\n\n    private var sut: MyViewModel!\n    private var mockRepository: MockRepository!\n\n    override func setUp() {\n        super.setUp()\n        mockRepository = MockRepository()\n        sut = MyViewModel(repository: mockRepository)\n    }\n\n    override func tearDown() {\n        sut = nil\n        mockRepository = nil\n        super.tearDown()\n    }\n\n    func testLoadData_setsStateToSuccess_whenRepositoryReturnsData() async {\n        \u002F\u002F Given\n        mockRepository.stubbedResult = [Item(id: \"1\", name: \"Test\")]\n\n        \u002F\u002F When\n        await sut.loadData()\n\n        \u002F\u002F Then\n        XCTAssertEqual(sut.state, .success([Item(id: \"1\", name: \"Test\")]))\n    }\n\n    func testLoadData_setsStateToError_whenRepositoryThrows() async {\n        \u002F\u002F Given\n        mockRepository.stubbedError = NSError(domain: \"\", code: -1)\n\n        \u002F\u002F When\n        await sut.loadData()\n\n        \u002F\u002F Then\n        if case .error = sut.state {\n            \u002F\u002F Expected\n        } else {\n            XCTFail(\"Expected error state\")\n        }\n    }\n}\n```\n\n### Naming Conventions\n\n```swift\n\u002F\u002F Pattern: testMethodName_expectedBehavior_whenCondition\nfunc testCalculateTotal_returnsZero_whenCartIsEmpty() { ... }\nfunc testFormatPrice_throws_whenAmountIsNegative() { ... }\n```\n\n### Async Testing\n\n```swift\n\u002F\u002F async\u002Fawait\nfunc testFetchData_returnsItems() async throws {\n    let items = try await sut.fetchData()\n    XCTAssertFalse(items.isEmpty)\n}\n\n\u002F\u002F Combine\nfunc testPublisher_emitsValue() {\n    let expectation = expectation(description: \"Value emitted\")\n    var received: String?\n\n    sut.publisher\n        .sink { value in\n            received = value\n            expectation.fulfill()\n        }\n        .store(in: &cancellables)\n\n    sut.trigger()\n    wait(for: [expectation], timeout: 1.0)\n    XCTAssertEqual(received, \"expected\")\n}\n```\n\n### Manual Mocking (No Framework)\n\n```swift\nclass MockRepository: RepositoryProtocol {\n    var stubbedResult: [Item] = []\n    var stubbedError: Error?\n    var fetchDataCallCount = 0\n\n    func fetchData() async throws -> [Item] {\n        fetchDataCallCount += 1\n        if let error = stubbedError { throw error }\n        return stubbedResult\n    }\n}\n```\n\n## XCUITest (UI Testing)\n\n```swift\nfinal class MyAppUITests: XCTestCase {\n    let app = XCUIApplication()\n\n    override func setUp() {\n        super.setUp()\n        continueAfterFailure = false\n        app.launch()\n    }\n\n    func testLogin_navigatesToHome() {\n        app.textFields[\"Email\"].tap()\n        app.textFields[\"Email\"].typeText(\"test@test.com\")\n        app.secureTextFields[\"Password\"].tap()\n        app.secureTextFields[\"Password\"].typeText(\"password\")\n        app.buttons[\"Login\"].tap()\n\n        XCTAssertTrue(app.staticTexts[\"Welcome\"].waitForExistence(timeout: 5))\n    }\n}\n```\n\n## Running Tests\n\n```bash\n# All unit tests\nxcodebuild test -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 15 Pro' -quiet\n\n# Specific test class\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppTests\u002FMyViewModelTests\n\n# Specific test method\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppTests\u002FMyViewModelTests\u002FtestLoadData_setsStateToSuccess\n\n# UI tests\nxcodebuild test -scheme MyApp -destination '...' -only-testing:MyAppUITests\n```\n\n## Quick\u002FNimble (BDD Style)\n\n```swift\nimport Quick\nimport Nimble\n@testable import MyApp\n\nclass MyViewModelSpec: QuickSpec {\n    override class func spec() {\n        describe(\"MyViewModel\") {\n            var sut: MyViewModel!\n\n            beforeEach { sut = MyViewModel() }\n\n            context(\"when data loads successfully\") {\n                it(\"sets state to success\") {\n                    expect(sut.state).toEventually(equal(.success))\n                }\n            }\n        }\n    }\n}\n```\n\n---\n\n**Want to improve this skill?** Add your testing patterns, mocking strategies, and CI tips via a PR.\n",{"name":5,"license":15,"platforms":48,"description":7,"compatibility":49},[24],[37,34,28,41],"main","2edc6ac1cde2cc280ae0388aa8072902d2e230d5","skills\u002Fios\u002Fskills\u002Fmobiai-ios-testing",13,"https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core","https:\u002F\u002Fgithub.com\u002FArisGuimera","https:\u002F\u002Faristi.dev","⭐️ GitHub Star\r\n📲 Mobile developer\r\n🎥 Te enseño a programar apps en Youtube (AristiDevs)\r\n✍🏻 Y por escrito en CursoKotlin.com\r\n❤️ Kotlin Fanboy\r\n👇👇👇👇","2026-11-09 14:18:52",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add ArisGuimera\u002FMobiAI-Core --skill mobiai-ios-testing",{"tool":65,"command":66},"bunx","bunx skills add ArisGuimera\u002FMobiAI-Core --skill mobiai-ios-testing",{"tool":68,"command":69},"pnpm","pnpm dlx skills add ArisGuimera\u002FMobiAI-Core --skill mobiai-ios-testing","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fios\u002Fskills\u002Fmobiai-ios-testing\u002FSKILL.md",true,[],[74,95,108,126,140,160],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":84,"publishedAt":43},"01M6KCMR2WVBEC7T0YY4J2SCAR","mobiai-react-native","Use when working on a React Native project — building, testing, debugging, understanding navigation and state management.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Freact-native\u002Fskills\u002Fmobiai-react-native\u002FSKILL.md",[80,81],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":82,"categoryName":83,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发",[85,86,87,88,27,41,28,89,90,29,91,92,34,36,93,94,37,38],"react","react native","native","mobiai react native","debugging","state management","state","management","navigation","react-native",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":102,"publishedAt":43},"01M6KCMKFC3DKFW5B2208SM59W","mobiai-kmp","Use when working on a Kotlin Multiplatform project — shared code, expect\u002Factual declarations, platform-specific implementations, building and testing.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fkmp\u002Fskills\u002Fmobiai-kmp\u002FSKILL.md",[101],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[103,27,41,28,29,104,105,34,36,37,106,38,107],"mobiai kmp","kotlin","kotlin multiplatform","shared","platform",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":114,"publishedAt":43},"01M6KCM78C8P9R6X78EZCQ0YF9","mobiai-ios-device","Use when interacting with an iOS Simulator — run simctl commands, capture screenshots, read device logs, manage simulators.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fios\u002Fskills\u002Fmobiai-ios-device\u002FSKILL.md",[],[24,115,116,117,41,28,118,29,119,120,121,34,36,122,123,37,124,125],"device","mobiai ios device","screenshots","run","logs","capture","read","manage","ios simulator","commands","with",{"skillNo":127,"name":128,"nameZh":6,"description":129,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":130,"categories":131,"keywords":132,"publishedAt":43},"01M6KCM2HE6Z1B7WREJ9DTKKWA","mobiai-ios-build","Use when building an iOS project, configuring schemes, troubleshooting build failures, or managing dependencies with CocoaPods or SPM.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fios\u002Fskills\u002Fmobiai-ios-build\u002FSKILL.md",[],[24,133,134,135,41,28,29,136,34,36,137,37,38,125,138,139],"build","ios build","mobiai ios build","troubleshooting","dependencies","spm","cocoapods",{"skillNo":141,"name":142,"nameZh":6,"description":143,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":144,"categories":145,"keywords":146,"publishedAt":43},"01M6KCKWZNT8P6K5SYXE1YAT9E","mobiai-ios-architecture","Use when creating features, refactoring, or navigating an iOS codebase — understand project structure, find where to make changes, follow existing patterns.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fios\u002Fskills\u002Fmobiai-ios-architecture\u002FSKILL.md",[],[147,24,148,149,41,150,28,29,151,152,34,36,153,154,38,155,156,157,158,159],"architecture","ios architecture","mobiai ios architecture","project structure","refactoring","find","patterns","structure","understand","changes","codebase","Existing","features",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":167,"publishedAt":43},"01M6KCKBZ4CGTMW6GCAZW54QRM","mobiai-flutter","Use when working on a Flutter or Dart project — building, testing, debugging, understanding state management and project structure.","https:\u002F\u002Fgithub.com\u002FArisGuimera\u002FMobiAI-Core\u002Fblob\u002Fmain\u002Fskills\u002Fflutter\u002Fskills\u002Fmobiai-flutter\u002FSKILL.md",[166],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},[168,169,27,41,150,28,89,90,29,91,170,92,34,36,154,37,38],"flutter","mobiai flutter","dart"]