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.
Smart router to testing patterns and practices. Writing unit tests, creating mocks, test organization.
This skill activates when working with:
import Testing, @Test, @Suite) for NEW testsProductionCode: SetContentViewDataBuilder.swift
Test File: SetContentViewDataBuilderTests.swift
Location: AnyTypeTests/[Category]/[TestFile].swift
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()
}
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)
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")
}
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: ""
)
}
}
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")
}
}
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)
}
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")
}
@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)
}
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)
}
Location: Anytype/Sources/PreviewMocks/
Usage:
import SwiftUI
#Preview {
MockView {
// Configure mock state
SpaceViewsStorageMock.shared.workspaces = [...]
} content: {
MyView()
}
}
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
}
}
When writing tests, ensure:
testFeature_Condition_ExpectedBehavior!) - use proper assertions@testable import Anytype)CRITICAL: Always update tests when refactoring:
rg "OldClassName" AnyTypeTests/ --type swift
rg "oldPropertyName" AnyTypeTests/ --type swift
Update test mocks:
AnyTypeTests/Mocks/Anytype/Sources/PreviewMocks/MockView.swiftUpdate mock extensions:
.mock( in test filesRun tests before committing:
@Test func testBuilderCreatesCorrectModel() {
let input = [...setup...]
let result = builder.build(input)
#expect(result.property1 == expected1)
#expect(result.property2 == expected2)
#expect(result.collection.count == 3)
}
@Suite(.serialized)
struct StorageTests {
init() {
// Setup mock dependencies
}
@Test func testSaveAndRetrieve() {
storage.save(item)
let retrieved = storage.get(item.id)
#expect(retrieved?.id == item.id)
}
}
@Test func testParseValidInput() {
let result = parser.parse("valid input")
#expect(result != nil)
}
@Test func testParseInvalidInput_ReturnsNil() {
let result = parser.parse("")
#expect(result == nil)
}
@Test func testCountersPropagation() {
var model = Model()
model.state = createState(messages: 5, mentions: 2)
#expect(model.unreadCounter == 5)
#expect(model.mentionCounter == 2)
}
Full working example: AnyTypeTests/Services/SetContentViewDataBuilderTests.swift
Full working example: AnyTypeTests/Services/ChatMessageLimitsTests.swift
Navigation: This is a smart router. For comprehensive testing guidelines and architecture patterns, refer to IOS_DEVELOPMENT_GUIDE.md.
Quick help: Just ask "How do I test X?" or "Create tests for Y feature"
npx skills add anyproto/tests-developer下载完整 Skill 目录,包含 SKILL.md 及所有相关文件
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer