tests-developer

0
0
Source

Smart router to testing patterns and practices. Use when writing unit tests, creating mocks, testing edge cases, or working with Swift Testing and XCTest frameworks.

Install

mkdir -p .claude/skills/tests-developer && curl -L -o skill.zip "https://mcp.directory/api/skills/download/6982" && unzip -o skill.zip -d .claude/skills/tests-developer && rm skill.zip

Installs to .claude/skills/tests-developer

About this skill

Tests Developer Skill

Smart router to testing patterns and practices. Writing unit tests, creating mocks, test organization.

When to Use This Skill

This skill activates when working with:

  • Writing unit tests
  • Creating test mocks
  • Testing edge cases
  • Test-driven development (TDD)
  • Test refactoring and updates
  • Swift Testing framework usage
  • XCTest framework usage

Quick Reference

Critical Rules

  • Use Swift Testing framework (import Testing, @Test, @Suite) for NEW tests
  • Keep existing XCTest tests as-is (don't migrate unless necessary)
  • Test edge cases: nil values, empty collections, boundary conditions
  • Create mock helpers in test file extensions when needed
  • Update tests when refactoring - always search and update references
  • Never skip tests for data transformation or business logic
  • Never use force unwrapping in tests - use proper assertions

Test File Naming

ProductionCode:  SetContentViewDataBuilder.swift
Test File:       SetContentViewDataBuilderTests.swift
Location:        AnyTypeTests/[Category]/[TestFile].swift

Swift Testing Framework (Preferred for New Tests)

Basic Structure:

import Testing
import Foundation
@testable import Anytype
import Services

@Suite
struct MyFeatureTests {

    private let sut: MyFeature  // System Under Test

    init() {
        self.sut = MyFeature()
    }

    @Test func testSpecificBehavior() {
        // Arrange
        let input = "test"

        // Act
        let result = sut.process(input)

        // Assert
        #expect(result == "expected")
    }

    @Test func testEdgeCase_EmptyInput_ReturnsNil() {
        let result = sut.process("")
        #expect(result == nil)
    }
}

Suite Options:

@Suite                      // Parallel execution (default)
@Suite(.serialized)        // Sequential execution (for shared state)

Assertions:

#expect(value == expected)           // Equality
#expect(value != unexpected)         // Inequality
#expect(result != nil)               // Not nil
#expect(array.isEmpty)               // Boolean conditions
#expect(throws: SomeError.self) {    // Error throwing
    try throwingFunction()
}

XCTest Framework (Legacy Tests)

Basic Structure:

import XCTest
@testable import Anytype

final class MyFeatureTests: XCTestCase {

    var sut: MyFeature!

    override func setUpWithError() throws {
        sut = MyFeature()
    }

    override func tearDownWithError() throws {
        sut = nil
    }

    func testSpecificBehavior() {
        // Arrange
        let input = "test"

        // Act
        let result = sut.process(input)

        // Assert
        XCTAssertEqual(result, "expected")
    }
}

Common Assertions:

XCTAssertEqual(actual, expected)
XCTAssertNotEqual(actual, unexpected)
XCTAssertNil(value)
XCTAssertNotNil(value)
XCTAssertTrue(condition)
XCTAssertFalse(condition)
XCTAssertThrowsError(try expression)

Test Organization Patterns

1. Edge Case Testing (Critical)

Always test these scenarios:

@Test func testEmptyInput() {
    let result = sut.process([])
    #expect(result.isEmpty)
}

@Test func testNilInput() {
    let result = sut.process(nil)
    #expect(result == nil)
}

@Test func testSingleItem() {
    let result = sut.process([item])
    #expect(result.count == 1)
}

@Test func testBoundaryCondition() {
    let items = (0..<100).map { Item(id: "\($0)") }
    let result = sut.process(items)
    #expect(result.count <= 100)
}

@Test func testTruncation_LimitsToMax() {
    let attachments = (0..<5).map { ObjectDetails.mock(id: "item\($0)") }
    let result = sut.truncate(attachments, limit: 3)
    #expect(result.count == 3)
    #expect(result[0].id == "item0")
    #expect(result[2].id == "item2")
}

2. Mock Helpers (In Test File)

Location: Create as extensions in the same test file

// At bottom of test file
extension ObjectDetails {
    static func mock(id: String) -> ObjectDetails {
        ObjectDetails(id: id, values: [:])
    }
}

extension Participant {
    static func mock(
        id: String,
        globalName: String = "",
        icon: ObjectIcon? = nil
    ) -> Participant {
        Participant(
            id: id,
            localName: "",
            globalName: globalName,
            icon: icon,
            status: .active,
            permission: .reader,
            identity: "",
            identityProfileLink: "",
            spaceId: "",
            type: ""
        )
    }
}

3. Dependency Injection in Tests

Using Factory Pattern:

@Suite(.serialized)  // Required for DI setup
struct MyFeatureTests {

    private let mockService: MyServiceMock

    init() {
        let mockService = MyServiceMock()
        Container.shared.myService.register { mockService }
        self.mockService = mockService
    }

    @Test func testWithMockedDependency() {
        mockService.expectedResult = "test"
        let sut = MyFeature()
        let result = sut.doWork()
        #expect(result == "test")
    }
}

4. Testing Protocols (Make Methods Internal)

Problem: Private methods can't be tested Solution: Use internal access and test via protocol

// Production code - SetContentViewDataBuilder.swift
final class SetContentViewDataBuilder: SetContentViewDataBuilderProtocol {

    // ✅ Internal for testing (not private)
    func buildChatPreview(
        objectId: String,
        spaceView: SpaceView?,
        chatPreviewsDict: [String: ChatMessagePreview]
    ) -> MessagePreviewModel? {
        // Implementation
    }
}

// Test code
@Test func testBuildChatPreview_EmptyDict_ReturnsNil() {
    let result = builder.buildChatPreview(
        objectId: "test",
        spaceView: nil,
        chatPreviewsDict: [:]
    )
    #expect(result == nil)
}

5. Dictionary Conversion Testing

Performance validation:

@Test func testDictionaryConversion_EmptyArray() {
    let items: [Item] = []
    let dict = Dictionary(uniqueKeysWithValues: items.map { ($0.id, $0) })
    #expect(dict.isEmpty)
}

@Test func testDictionaryConversion_MultipleItems() {
    let items = (0..<10).map { Item(id: "item\($0)") }
    let dict = Dictionary(uniqueKeysWithValues: items.map { ($0.id, $0) })

    #expect(dict.count == 10)
    for i in 0..<10 {
        #expect(dict["item\(i)"] != nil)
    }
}

@Test func testDictionaryLookup_O1Performance() {
    let items = (0..<100).map { Item(id: "item\($0)") }
    let dict = Dictionary(uniqueKeysWithValues: items.map { ($0.id, $0) })

    let result = dict["item50"]
    #expect(result != nil)
    #expect(result?.id == "item50")
}

6. Testing with Dates

@Test func testDateFormatting() {
    let date = Date(timeIntervalSince1970: 1700000000)
    let result = formatter.format(date)
    #expect(result.isEmpty == false)
}

@Test func testDateComparison() {
    let now = Date()
    let future = now.addingTimeInterval(3600)
    #expect(future > now)
}

7. Testing Protobuf Models

ChatState example:

@Test func testChatStateCounters() {
    var chatState = ChatState()

    var messagesState = ChatState.UnreadState()
    messagesState.counter = 5
    chatState.messages = messagesState

    var mentionsState = ChatState.UnreadState()
    mentionsState.counter = 2
    chatState.mentions = mentionsState

    #expect(chatState.messages.counter == 5)
    #expect(chatState.mentions.counter == 2)
}

Mock Services Pattern

Preview Mocks (for SwiftUI Previews)

Location: Anytype/Sources/PreviewMocks/

Usage:

import SwiftUI

#Preview {
    MockView {
        // Configure mock state
        SpaceViewsStorageMock.shared.workspaces = [...]
    } content: {
        MyView()
    }
}

Test Mocks (for Unit Tests)

Location: AnyTypeTests/Mocks/ or in test file

Pattern:

@testable import Anytype

final class MyServiceMock: MyServiceProtocol {
    var callCount = 0
    var capturedInput: String?
    var stubbedResult: Result?

    func process(_ input: String) -> Result {
        callCount += 1
        capturedInput = input
        return stubbedResult ?? .default
    }
}

Testing Checklist

When writing tests, ensure:

  • Test happy path (valid input, expected output)
  • Test edge cases (nil, empty, boundary conditions)
  • Test error conditions (invalid input, throwing functions)
  • Test data transformations (truncation, filtering, mapping)
  • Test performance assumptions (O(1) lookups, O(n) operations)
  • Create mock helpers for complex types
  • Use descriptive test names: testFeature_Condition_ExpectedBehavior
  • Follow AAA pattern: Arrange, Act, Assert
  • No force unwrapping (!) - use proper assertions
  • Import only necessary modules (@testable import Anytype)

When Refactoring Production Code

CRITICAL: Always update tests when refactoring:

  1. Search for test references:
rg "OldClassName" AnyTypeTests/ --type swift
rg "oldPropertyName" AnyTypeTests/ --type swift
  1. Update test mocks:

    • Check AnyTypeTests/Mocks/
    • Check Anytype/Sources/PreviewMocks/
    • Update DI registrations in MockView.swift
  2. Update mock extensions:

    • Search for .mock( in test files
    • Update parameters if initializer changed
  3. Run tests before committing:

    • User will verify in Xcode (faster with caches)
    • Report all test file changes to user

Common Test Patterns in Codebase

Pattern 1: Builder Testing

@Test func testBuilderCreatesCorrectModel() {
    let input = [...setup...]
    let result = builder.build(input)

    #expect(result.property1 == expected1)
    #expect(result.property2 == expected2)
    #expect(result.collection.count == 3)
}

Pattern 2: Storage/Repository Testing

@Suite(.serialized)
struct StorageTests {
    init() {
        // Setup

---

*Content truncated.*

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

642969

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

590705

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

318398

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

339397

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

451339

fastapi-templates

wshobson

Create production-ready FastAPI projects with async patterns, dependency injection, and comprehensive error handling. Use when building new FastAPI applications or setting up backend API projects.

304231

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.