The test target is MeshtasticTests/. All new tests must use Swift Testing (import Testing).
import Testing
@testable import Meshtastic
@Suite("MyFeatureTests")
struct MyFeatureTests {
@Test func someExpectation() {
let value = computeSomething()
#expect(value == 42)
}
@Test func requiredValue() throws {
let result = try #require(optionalValue())
#expect(result.count > 0)
}
}
@Suite to group related tests under a descriptive name.#expect for assertions (does not throw on failure — test continues).#require for preconditions (throws on failure — test stops).XCTAssert* in new test files.Run with ⌘U in Xcode. There is no CLI test runner — tests require Xcode.
Ensure all existing tests pass before opening a PR. SwiftLint runs on every commit; tests failing due to lint errors will block CI.
Snapshot tests for SwiftUI views live in MeshtasticTests/SwiftUIViewSnapshotTests.swift.
renderImage helper renders a SwiftUI view to a UIImage using UIHostingController + drawHierarchy(in:afterScreenUpdates:true).forDocs: true are saved to docs/assets/screenshots/ (shared with the documentation site); test-only snapshots are saved to MeshtasticTests/__Snapshots__/.CGImage dimensions.copy-snapshots.sh copies only doc-referenced PNGs into the app bundle — test-only snapshots are never bundled.@Suite("MyViewSnapshotTests")
struct MyViewSnapshotTests {
@Test func rendersCorrectly() throws {
let image = try renderImage(MyView(), width: 390)
let cgImage = try #require(image.cgImage)
#expect(cgImage.width == 390 * Int(UIScreen.main.scale))
}
}
<ViewName>SnapshotTests.cgImage.width / cgImage.height (pixel dimensions at screen scale), not UIImage.size (which is scale-dependent).ScrollView or no intrinsic height, pass an explicit height: parameter to renderImage.When a view is snapshotted in both colour schemes (e.g. foo_light.png + foo_dark.png), embedding both ![]() tags side-by-side causes both images to appear simultaneously on the Jekyll site and in the in-app viewer. Use an HTML <picture> element instead:
<picture>
<source media="(prefers-color-scheme: dark)" srcset="../assets/screenshots/foo_dark.png" />
<img src="../assets/screenshots/foo_light.png" alt="Description" />
</picture>
This works in both contexts because build-docs.sh invokes cmark-gfm --unsafe (raw HTML is passed through) and WKWebView (used for in-app display) is full WebKit and respects prefers-color-scheme.
Delete the reference PNG and run the test once — it records a new reference. Commit the new reference with your PR.
For tests involving async/await:
@Test func asyncOperation() async throws {
let result = await someAsyncFunction()
#expect(result != nil)
}
Router is @MainActor; access it in tests with await MainActor.run { }:
@Test func routerNavigates() async {
let router = await MainActor.run { Router() }
await MainActor.run { router.routeSettings(path: "helpDocs") }
let state = await MainActor.run { router.navigationState.settingsNavigationState }
#expect(state == .helpDocs)
}