This guide explains how to write and run tests for the TableCapture app.
The project uses two types of tests:
- Framework: Swift Testing (modern, introduced in Swift 5.9+)
- Purpose: Test individual functions, logic, and data transformations
- Speed: Fast (no UI, no app launch)
- Location:
TableCaptureTests/TableCaptureTests.swift
- Framework: XCTest + XCUITest
- Purpose: Test user interactions and full app behavior
- Speed: Slower (launches full app)
- Location:
TableCaptureUITests/TableCaptureUITests.swift
Run all tests:
- Press
⌘U(Command + U) - Or: Product → Test
Run a single test:
- Click the diamond icon next to the test function
- Or: Put cursor in test function and press
⌘U
Run a specific test file:
- Click the diamond icon next to the
structorclassname
# Run all tests
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture
# Run only unit tests
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -only-testing:TableCaptureTests
# Run only UI tests
xcodebuild test -project TableCapture.xcodeproj -scheme TableCapture -only-testing:TableCaptureUITestsimport Testing
@testable import TableCapture
struct MyTests {
@Test("Description of what this tests")
func testSomething() async throws {
// Arrange: Set up test data
let input = "test"
// Act: Perform the action
let result = someFunction(input)
// Assert: Verify the result
#expect(result == "expected")
}
}Assertions:
#expect(value == expected) // Basic equality
#expect(value != unwanted) // Inequality
#expect(array.contains(item)) // Contains check
#expect(value > 0) // Comparison
#expect(value == nil) // Nil checkAsync tests:
@Test func testAsync() async throws {
let result = await someAsyncFunction()
#expect(result.isSuccess)
}Test with parameters:
@Test(arguments: [1, 2, 3, 4, 5])
func testMultipleInputs(number: Int) {
#expect(number > 0)
}import XCTest
final class MyUITests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
@MainActor
func testSomething() throws {
// Find UI elements
let button = app.buttons["My Button"]
// Interact with them
button.tap()
// Verify results
XCTAssertTrue(button.exists)
}
}Finding elements:
app.buttons["Button Title"] // Button by label
app.textFields["Email"] // Text field
app.windows["Window Title"] // Window
app.staticTexts["Label"] // Text labelInteractions:
element.tap() // Click
element.typeText("hello") // Type text
element.swipeLeft() // Swipe gestureAssertions:
XCTAssertTrue(element.exists)
XCTAssertEqual(element.label, "Expected")
XCTAssertFalse(element.isEnabled)
XCTAssertNotNil(value)CSV Formatting:
- ✅ Escaping commas
- ✅ Escaping quotes
- ✅ Handling empty cells
Markdown Formatting:
- ✅ Table structure (headers, separators)
- ✅ Escaping pipe characters
- ✅ Handling uneven row lengths
Grid Management:
- ✅ Adding columns/rows
- ✅ Removing selected lines
- ✅ Clearing all lines
App Launch:
- ✅ Launches without crashing
- ✅ Stays running after launch
Performance:
- ✅ Launch time measurement
- ✅ Memory usage measurement
Accessibility:
- ✅ VoiceOver support checks
- Keep tests isolated - Each test should be independent
- Use descriptive names - Test names should explain what they verify
- Test one thing - Each test should verify a single behavior
- Use
@testable import- Access internal types for testing
- Use accessibility identifiers - Make elements easier to find
- Wait for elements - Use
waitForExistence(timeout:) - Test user journeys - Simulate real user workflows
- Keep tests stable - Avoid flaky tests with proper waits
- Run tests before commits - Ensure nothing breaks
- Write tests for bugs - Prevent regressions
- Test edge cases - Empty strings, nil values, extreme inputs
- Keep tests maintainable - Refactor test code too
@Test("What this function should do")
func testNewFunction() async throws {
let viewModel = TableEditorViewModel(image: createTestImage())
// Test your function
viewModel.newFunction()
#expect(viewModel.someProperty == expectedValue)
}@MainActor
func testNewUIFeature() throws {
let app = XCUIApplication()
app.launch()
// Find and interact with your UI
let newButton = app.buttons["New Feature"]
newButton.tap()
// Verify the result
XCTAssertTrue(app.staticTexts["Success"].exists)
}Tests won't run:
- Clean build folder:
⌘⇧K - Delete derived data:
Xcode → Settings → Locations → Derived Data
UI tests can't find elements:
- Add accessibility identifiers to SwiftUI views:
Button("My Button") { } .accessibilityIdentifier("myButton")
- Use
po app.debugDescriptionin debugger to see all elements
Tests are flaky:
- Add explicit waits:
let element = app.buttons["My Button"] XCTAssertTrue(element.waitForExistence(timeout: 5))