# SwiftUI ViewInspector tests with an AI prompt: a guide

> By Lawrence Arya, Founder & CEO of VP0. Published 2026-06-10. 10 min read.
> Source: https://vp0.com/blogs/swiftui-view-inspector-ai-unit-testing-prompt-free-ios-template-vibe-coding-guid

ViewInspector lets you assert on a SwiftUI view in a plain unit test. Pair it with a tight AI prompt and injectable views, and the tests stay fast and real.

**TL;DR.** To unit test SwiftUI views, use ViewInspector, an open-source library that reads and asserts on a view's contents in a plain unit test with no simulator. To generate the tests with AI, give Claude Code or Cursor the view, the real ViewInspector API, and a rule to assert on behavior rather than pixels. The bigger lever is building small, injectable views in the first place, which is far easier when you start from a free VP0 design than from a blank prompt.

To unit test SwiftUI views, the practical tool is [ViewInspector](https://github.com/nalexn/ViewInspector), an open-source library with more than 2,600 stars that lets you read and assert on a view's contents in a plain unit test, with no simulator and no UI test runner. The reliable way to generate those tests with AI is to give Claude Code or Cursor the view, the ViewInspector API, and a strict rule to assert on structure and behavior rather than pixels. The tests are only as good as the views, though, so the bigger lever is building small, injectable screens in the first place, which is far easier when you start from a real design instead of generating a tangled view from a blank prompt.

The sections below cover what ViewInspector does, how to prompt an AI builder to write the tests, where the generated tests go wrong, and how to make your SwiftUI testable from the start.

## What is ViewInspector and why use it for SwiftUI tests?

ViewInspector is a library that traverses a SwiftUI view's hierarchy at test time so you can assert on it directly. SwiftUI views are value types with an opaque `body`, which is why you cannot inspect them with normal reflection, and it is the gap ViewInspector fills using its own traversal. You can find a child view, read the text inside it, trigger a button's action, toggle a binding, and confirm the resulting state, all inside a standard unit test.

The reason this matters is speed and focus. A [XCUITest](https://developer.apple.com/documentation/xctest) launches the whole app in the simulator, drives the real UI, and takes seconds per test, which makes a large suite slow and flaky. A ViewInspector test runs in-process in milliseconds and checks one specific thing, so you can have hundreds of them and still get fast feedback. It works with both [XCTest](https://developer.apple.com/documentation/xctest) and Apple's newer Swift Testing framework, so it fits whichever runner your project uses.

It does not replace end-to-end UI tests. It replaces the much larger category of tests that only need to confirm a view shows the right content and reacts correctly to state, which is most of them.

## How do ViewInspector unit tests actually work?

A ViewInspector test calls `.inspect()` on the view, navigates to the part you care about, and asserts on it. The shape is short once the pattern clicks.

```swift
import ViewInspector
import Testing
@testable import MyApp

@Test func priceLabelShowsTotal() throws {
    let model = CartModel(items: [Item(price: 9), Item(price: 3)])
    let view = CartSummary(model: model)
    let total = try view.inspect().find(text: "Total: 12")
    #expect(try total.string() == "Total: 12")
}
```

You can also drive interaction: find a button, call its `tap()`, and then assert that the model changed. For views whose content depends on `@State` that updates asynchronously, ViewInspector provides an inspection callback so the assertions run after the state settles rather than racing it. The official [SwiftUI documentation](https://developer.apple.com/documentation/swiftui) describes how state drives the view, and ViewInspector is what lets you verify that wiring without rendering to a screen.

The mental model is to test the contract of the view: given this state, it shows this content and these controls, and tapping a control changes state in this way. That contract is stable even when layout details change, which keeps the tests from breaking on every visual tweak.

## Writing an AI prompt that generates ViewInspector tests

A good prompt names the library, points the model at the real view, and forbids the two things AI gets wrong most: inventing API and testing the wrong layer. Paste the view's source, then constrain the request:

```text
Write unit tests for this SwiftUI view using ViewInspector and Swift Testing.
Rules:
- Use only real ViewInspector API: inspect(), find(text:), find(ViewType...), tap(), callOnChange. Do not invent methods.
- Test behavior and content, not layout or exact pixel positions.
- For each test, set up the model, render the view, assert on what the user would see, then assert on state after interaction.
- Inject the model through the initializer; do not reach into private state.
- Cover the empty state, a populated state, and one interaction.
Return one test per behavior with a descriptive name.
```

The constraints carry the prompt. Telling the model to use only real ViewInspector methods cuts down hallucinated API, and telling it to assert on behavior keeps the tests from breaking on cosmetic changes. If your AI builder still produces methods that do not exist, a short read on [why Cursor hallucinates SwiftUI](/blogs/how-to-fix-cursor-ai-hallucinating-swiftui/) covers how to ground it in the real API.

## What AI gets wrong in SwiftUI tests

Generated SwiftUI tests fail in a few predictable ways, and knowing them turns a broken first draft into a quick fix. The most common is invented API: the model writes `view.inspect().button("Save")` or some plausible-looking method ViewInspector does not have, because it is pattern-matching other testing libraries. Replace it with the real `find` and `tap` calls.

The second is testing the wrong layer. AI often asserts on exact spacing, frame sizes, or view order by index, which produces tests that break the moment you adjust the layout. Good ViewInspector tests find views by their content or type, not by position. The third is ignoring async state, where the test asserts before an `@State` update has applied; the fix is the inspection callback that waits for the state to settle.

A fourth, subtler one is tests that pass for the wrong reason, asserting that a view exists without checking what it contains. Those give false confidence. Reviewing generated tests the same way you would review generated code is worth the time, and a look at running a [SwiftUI code audit](/blogs/swiftui-code-audit-service/) covers what to check beyond tests.

## Making your SwiftUI testable in the first place

Tests are only as good as the views, and the most testable views share three traits: they are small, they take their dependencies through the initializer, and they keep logic out of `body`. A 300-line view that reads a global singleton is hard to test no matter how good the tooling, because you cannot set up its state in isolation. A small view that receives an `@Observable` model can be rendered with any state you want and checked directly.

This is where the starting point matters more than the test framework. When you build screens from a real design, they come out structured and consistent, which makes them straightforward to test; when you generate them from a vague prompt, they come out tangled, and the tests inherit the mess. Starting from a free VP0 design gives the AI builder real structure to implement, because each design has a machine-readable source page Claude Code, Cursor, or Rork can read from a pasted link. The views you get are easier to both build and test, and you can pair this with the broader [advanced SwiftUI patterns](/blogs/swiftui-tutorial-zaawansowany/) that keep state ownership clean. Clean state ownership is exactly what makes a view injectable, and injectable is what makes it testable.

## ViewInspector vs XCUITest vs snapshot testing

Each SwiftUI testing approach answers a different question, so the strongest suites use more than one. The split is clearest as a table.

| Approach | Best for | Speed | Breaks when |
|---|---|---|---|
| ViewInspector | Content and behavior of a view given state | Fast, in-process | The view's contract changes |
| XCUITest | Full user flows across screens | Slow, simulator | A flow or navigation path changes |
| Snapshot testing | Catching unintended visual changes | Medium | The design intentionally changes |

Use ViewInspector for the bulk of your tests, the per-view checks that confirm content and interaction. Add a small number of XCUITests for the critical end-to-end flows like sign-in and checkout. Add snapshot tests when visual regressions are a real risk and you want a pixel diff to catch them. Leaning entirely on XCUITest is the common mistake, because the suite gets slow and flaky long before it gets thorough.

## Common ViewInspector setup issues

A few setup details trip people up, and they are quick to fix once you know them. Tests need `@testable import` of your app module so ViewInspector can reach internal views, plus `import ViewInspector` itself. Recent versions of the library dropped the old requirement to add an `Inspectable` conformance to every view, so if you are following an older tutorial that tells you to write `extension MyView: Inspectable {}`, you can usually skip it on a current version; check the [library's README](https://github.com/nalexn/ViewInspector) for the version you installed.

The other frequent issue is asynchronous content. When a view loads data into `@State` and then renders it, an immediate assertion runs before the state updates, so the test fails intermittently. The fix is the inspection callback that fires after the update, rather than reading the view synchronously. Flaky async tests are nearly always this, not a bug in the view.

## Key takeaways: unit testing SwiftUI with ViewInspector and AI

Use ViewInspector for fast, in-process tests that assert on a view's content and behavior, and keep XCUITest for a handful of critical flows. Prompt your AI builder with the real ViewInspector API and a rule to test behavior over layout, then review the generated tests for invented methods and assertions that pass for the wrong reason. Most of all, build small, injectable views, since testability is decided when you write the view, not when you write the test. Starting from a free VP0 design gives you that structure for nothing, while a commissioned, fully-tested screen build can cost $5,000 or more.

You can [browse VP0 designs](/explore) to start from a screen that is already structured cleanly enough to test.

## Frequently asked questions

### What is the best way to write SwiftUI unit tests with an AI prompt?

Use ViewInspector as the library and prompt Claude Code or Cursor with the real view, the actual ViewInspector API, and a rule to assert on behavior and content rather than layout. Building the view from a free VP0 design first makes the tests easier, because a cleanly structured, injectable view is straightforward to set up in isolation. Then review the generated tests for invented methods and for assertions that only check a view exists without checking what it contains.

### How can I use ViewInspector to test an iOS app built with AI?

Add ViewInspector to your test target, import your app module with `@testable`, and write tests that render a view with a known model, find elements by content or type, and assert on the result. For interactions, find a control, call its action, and check the state afterward. The same approach works whether the view was hand-written or generated, as long as the view takes its dependencies through the initializer rather than reaching into globals.

### What is the safest way to generate SwiftUI tests with Claude Code or Cursor?

Constrain the model to real ViewInspector methods, require behavior-level assertions, and review every generated test before trusting it. AI frequently invents testing API and writes tests that pass without verifying anything meaningful, so treat generated tests as a first draft to audit, not a finished suite. Running them against an intentionally broken version of the view is a quick way to confirm they actually fail when they should.

### Can VP0 provide a free SwiftUI or React Native template to test against?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, and designs come in SwiftUI and React Native variants. Starting from a design produces a structured, injectable view, which is exactly the kind of view that is simple to cover with ViewInspector tests.

### What common errors happen when vibe coding SwiftUI tests?

The usual ones are hallucinated ViewInspector methods that do not exist, tests that assert on layout or view index and break on any cosmetic change, async tests that read state before it updates, and tests that confirm a view is present without checking its content. All four are easy to miss because the suite still goes green, so a manual pass over generated tests, plus running them against a deliberately broken view, catches the ones that are not really testing anything.

## Frequently asked questions

### What is the best way to write SwiftUI unit tests with an AI prompt?

Use ViewInspector as the library and prompt Claude Code or Cursor with the real view, the actual ViewInspector API, and a rule to assert on behavior and content rather than layout. Building the view from a free VP0 design first makes the tests easier, because a cleanly structured, injectable view is straightforward to set up in isolation. Then review the generated tests for invented methods and for assertions that only check a view exists without checking what it contains.

### How can I use ViewInspector to test an iOS app built with AI?

Add ViewInspector to your test target, import your app module with @testable, and write tests that render a view with a known model, find elements by content or type, and assert on the result. For interactions, find a control, call its action, and check the state afterward. The same approach works whether the view was hand-written or generated, as long as the view takes its dependencies through the initializer rather than reaching into globals.

### What is the safest way to generate SwiftUI tests with Claude Code or Cursor?

Constrain the model to real ViewInspector methods, require behavior-level assertions, and review every generated test before trusting it. AI frequently invents testing API and writes tests that pass without verifying anything meaningful, so treat generated tests as a first draft to audit, not a finished suite. Running them against an intentionally broken version of the view is a quick way to confirm they actually fail when they should.

### Can VP0 provide a free SwiftUI or React Native template to test against?

Yes. VP0 is a free iOS app design library where every design has a machine-readable source page an AI builder reads from a pasted link, and designs come in SwiftUI and React Native variants. Starting from a design produces a structured, injectable view, which is exactly the kind of view that is simple to cover with ViewInspector tests.

### What common errors happen when vibe coding SwiftUI tests?

The usual ones are hallucinated ViewInspector methods that do not exist, tests that assert on layout or view index and break on any cosmetic change, async tests that read state before it updates, and tests that confirm a view is present without checking its content. All four are easy to miss because the suite still goes green, so a manual pass over generated tests, plus running them against a deliberately broken view, catches the ones that are not really testing anything.

---
*Published on the [VP0 Journal](https://vp0.com/blogs). Free to read, index and cite with attribution.*
