Runtime introspection and unit testing of SwiftUI views

Overview

ViewInspector 🕵️‍♂️ for SwiftUI

Platform Build Status codecov

ViewInspector is a library for unit testing SwiftUI views. It allows for traversing a view hierarchy at runtime providing direct access to the underlying View structs.

Why?

SwiftUI view is a function of state. We could provide it with the input, but were unable to verify the output... Until now!

Helpful links

Use cases

1. Search the view of a specific type or condition

Use one of the find functions to quickly locate a specific view or assert there are none of such:

try sut.inspect().find(button: "Back")

try sut.inspect().findAll(ViewType.Text.self,
                          where: { try $0.attributes().isBold() })

Check out this section in the guide for the reference.

2. Read the inner state of the standard views

Standard SwiftUI views are no longer a black box:

let sut = Text("Completed by \(72.51, specifier: "%.1f")%").font(.caption)

let string = try sut.inspect().text().string(locale: Locale(identifier: "es"))
XCTAssertEqual(string, "Completado por 72,5%")

XCTAssertEqual(try sut.inspect().text().attributes().font(), .caption)

Each view has its own set of inspectable parameters, you can refer to the API coverage document to see what's available for a particular SwiftUI view.

3. Verify your custom view's state

Obtain a copy of your custom view with actual state and references from the hierarchy of any depth:

let sut = try view.inspect().find(CustomView.self).actualView()
XCTAssertTrue(sut.viewModel.isUserLoggedIn)

The library can operate with various types of the view's state, such as @Binding, @State, @ObservedObject and @EnvironmentObject.

4. Trigger side effects

You can simulate user interaction by programmatically triggering system-controls callbacks:

try sut.inspect().find(button: "Close").tap()

let list = try view.inspect().list()
try list[5].view(RowItemView.self).callOnAppear()

The library provides helpers for writing asynchronous tests for views with callbacks.

FAQs

Which views and modifiers are supported?

Check out the API coverage. There is currently almost full support for SwiftUI v1 API, the v2 and v3 support is under active development.

Is it using private APIs?

ViewInspector is using official Swift reflection API to dissect the view structures. So it'll be production-friendly even if you could somehow ship the test target to the production.

How do I add it to my Xcode project?

Assure you're adding the framework to your unit-test target. Do NOT add it to the main build target.

Swift Package Manager

https://github.com/nalexn/ViewInspector

Carthage

github "nalexn/ViewInspector"

CocoaPods

pod 'ViewInspector'

How do I use it in my project?

Please refer to the Inspection guide. You can also check out my other project that harnesses the ViewInspector for testing the entire UI.

Other questions, concerns or suggestions?

Ping me on Twitter or just submit an issue or a pull request on Github.


blog venmo

Comments
  • Inspecting Custom View Modifiers Issue

    Inspecting Custom View Modifiers Issue

    Consider the following ViewModifier:

    @available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
    private struct InspectableTestModifier: ViewModifier, Inspectable {
        
        @Binding var flag: Bool
        
        var didAppear: ((Self.Body) -> Void)?
        
        func body(content: Self.Content) -> some View {
            HStack {
                content
                Button(
                    action: { self.flag.toggle() },
                    label: { Text(flag ? "true" : "false") }
                )
            }
            .onAppear { self.didAppear?(self.body(content: content)) }
        }
    }
    

    And the following test:

        func testViewModifierOnFunction() throws {
            @Binding var flag = false
            var sut = InspectableTestModifier(flag: $flag)
            let exp = sut.on(\.didAppear) { body in
                let text1 = try body.find(relation: .child, traversal: .breadthFirst, skipFound: 0) {
                    try $0.text().string() == "false"
                }
                let text2 = try body.find(relation: .child, traversal: .breadthFirst, skipFound: 1) {
                    try $0.text().string() == "false"
                }
                
                var label1 = try body.hStack()
                    .button(1).labelView().text().string()
                var label2 = try body.hStack().modifier(InspectableTestModifier.self).hStack()
                    .button(1).labelView().text().string()
                print("\(text1.pathToRoot) = \(label1)")
                print("\(text2.pathToRoot) = \(label2)")
                
                try body.hStack().button(1).tap()
                
                label1 = try body.hStack()
                    .button(1).labelView().text().string()
                label2 = try body.hStack().modifier(InspectableTestModifier.self).hStack()
                    .button(1).labelView().text().string()
                print("\(text1.pathToRoot) = \(label1)")
                print("\(text2.pathToRoot) = \(label2)")
            }
            ViewHosting.host(view: Circle().modifier(sut))
            wait(for: [exp], timeout: 3.2)
        }
    

    Here's a result of running the test:

    Test Suite 'Selected tests' started at 2021-05-08 15:58:43.465
    Test Suite 'ViewInspectorTests.xctest' started at 2021-05-08 15:58:43.466
    Test Suite 'InspectionEmissaryTests' started at 2021-05-08 15:58:43.466
    Test Case '-[ViewInspectorTests.InspectionEmissaryTests testViewModifierOnFunction]' started.
    hStack().button(1).labelView().text() = false
    hStack().modifier(InspectableTestModifier.self).hStack().button(1).labelView().text() = false
    hStack().button(1).labelView().text() = false
    hStack().modifier(InspectableTestModifier.self).hStack().button(1).labelView().text() = true
    Test Case '-[ViewInspectorTests.InspectionEmissaryTests testViewModifierOnFunction]' passed (0.177 seconds).
    Test Suite 'InspectionEmissaryTests' passed at 2021-05-08 15:58:43.644.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.178) seconds
    Test Suite 'ViewInspectorTests.xctest' passed at 2021-05-08 15:58:43.644.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.179) seconds
    Test Suite 'Selected tests' passed at 2021-05-08 15:58:43.645.
    	 Executed 1 test, with 0 failures (0 unexpected) in 0.177 (0.180) seconds
    Program ended with exit code: 0
    

    I've been chasing this for a couple weeks and cannot unravel this mystery.

    Question #1, do you have any idea why the view generates the second instance of the label?

    Question #2, do you have any idea why the second instance of the label has the root-to-path that it has?

    Question #3, why does the button tap change the second instance of the label, but not the first?

    opened by gili-labs 22
  • Inspector emissary for view modifier

    Inspector emissary for view modifier

    @nalexn I have created this PR with the changes I made to InspectionEmissary to support view modifiers. It works great. However, the testViewModifierOnFunction test fails. I left it this way on purpose in order that you can look at the difference between this and the InspectionEmissaryForViewModifier. I do not see a difference. I dumped the body of the modifier in the debugger, and they seem identical and cannot ascertain why the ViewInspector would treat them differently.

    opened by gili-labs 18
  • Issue when inspecting views that interact with navigationBarItems

    Issue when inspecting views that interact with navigationBarItems

    Hi! Awesome work here, thank you! Given this view and its test:

    struct DetailView: View {
        var body: some View {
            EmptyView()
            .navigationBarItems(trailing: Text("Done")) // commenting this makes the test run fine.
        }
    }
    
    func testNavigationBarItems() {
      expect {
        // expected to not throw any error, got <notSupported("Please use \'navigationBarItems()\' for unwrapping the underlying view hierarchy.")>
        try detailView.body.inspect().emptyView()
      }.notTo(throwError())
      expect {
        // expected to not throw any error, got <modifierNotFound(parent: "DetailView", modifier: "navigationBarItems")>
        try detailView.inspect().navigationBarItems().emptyView()
      }.notTo(throwError())
    }
    

    Is this not supported, or am I missing something?

    opened by nobre84 17
  • Could not install on Xcode 12.2 beta

    Could not install on Xcode 12.2 beta

    I am on Xcode 12.2 beta and I could not install the package. After following the steps described in the documentation the "Finish" button did not become highlighted. Is this a known issue?

    Screenshot 2020-10-07 at 16 47 19 Thanks

    opened by multitudes 13
  • Click on alert button

    Click on alert button

    Hi @nalexn do you know if it is possible to click on an alert view button

    struct ContentView: View {
        
        @State private var showingAlert = false
    
        var body: some View {
            Button("Show Alert") {
                showingAlert = true
            }
            .alert(isPresented: $showingAlert) {
                Alert(title: Text("Important message"), message: Text("Wear sunscreen"), dismissButton: .default(Text("Got it!")))
            }
        }
    
    }
    

    what I've tried

        func testClickOnAlertButton() throws {
            // Given
            let view = ContentView()
            ViewHosting.host(view: view)
            
            let button = try view.inspect()
                .find(ContentView.self)
                .find(text: "Got it!")
                
            print(button)
        }
    

    How do I click on the Got it! button?

    opened by 2inqui 9
  • Error compiling unit tests on macOS

    Error compiling unit tests on macOS

    Hello,

    I've experiencing an issue writing SwiftUI unit tests on macOS 10.5.7 and 11.2.1, Xcode Version 12.4 (12D4e).

    A simple view:

    import SwiftUI
    
    struct ContentView: View {
        var body: some View {
            Text("Hello, World!")
        }
    }
    

    with a simple test case:

    import XCTest
    import ViewInspector
    
    @testable import ViewInspectorIssue
    
    extension ContentView: Inspectable { }
    
    class ContentViewTests: XCTestCase {
    
        func testExample() throws {
            let sut = ContentView()
        }
    
    }
    

    fails to compile with the error:

    xctest (19241) encountered an error...
    ...Underlying Error: The bundle “ViewInspectorIssueTests” couldn’t be loaded. The bundle couldn’t be loaded...
    ...Symbol not found: _$s7SwiftUI16PageTabViewStyleV16IndexDisplayModeVM...
    

    So, I cloned the repository and discovered those struct's are not being marked unavailable or ignored for macOS builds.

    I think the lines with:

    @available(iOS 14.0, tvOS 14.0, *)
    @available(macOS, unavailable)
    ...
    

    Should be:

    #if canImport(UIKit)
    @available(iOS 14.0, tvOS 14.0, *)
    ...
    #endif
    

    To test that theory, I've made those modifications locally and added the local repository using Xcode package manager with expected build results. :)

    opened by krissmi 9
  • Live Preview on Device not working

    Live Preview on Device not working

    After I'd added ViewInspector, Live Preview on Device stopped working, it seems:

    linker command failed with exit code 1 (use -v to see invocation)
    
    Build target ViewInspector:
    note: Set ENABLE_PREVIEWS=NO because SWIFT_OPTIMIZATION_LEVEL=-O, expected -Onone (in target 'ViewInspector' from project 'Pods')
    
    
    Link ViewInspector (arm64):
    ld: '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. file '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/XCTest.framework/XCTest' for architecture arm64
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    

    The regular Live Preview is working fine though

    opened by NeverwinterMoon 9
  • [Import error] No such module 'ViewInspector'

    [Import error] No such module 'ViewInspector'

    Xcode version: 11.4 Swift version: 5.1 Cocoapods version: 1.9.1

    Hello there!!

    I'm trying add this package into my project to SwiftUI tests targets, and I've found the error below:

    viewinspector-import-error

    I tried use this package with Swift package manager and with Cocoapods, and the library is not found with both package managers. I've tried import this package in two targets: Unit and UI Tests, and the problem is the same for both.

    I've added another library to my Xcode 11 project/workspace and works great!

    Do you know what I'm doing wrong?

    opened by mfdeveloper 9
  • Updating and accessing @State properties

    Updating and accessing @State properties

    I have the following view, in which an optional Text view is toggled in and out of visibility when the button is tapped:

    struct MyView: View {
    
        @State var showErrorMessage = false
    
        var body: some View {
            VStack {
                Button(action: {
                    self.showErrorMessage.toggle()
                    print("showErrorMessage = \(self.showErrorMessage)")
                }) {
                    Text("Click me.")
                }
    
                if showErrorMessage {
                    Text("ERROR")
                        .foregroundColor(Color.red)
                }
            }
        }
    }
    

    I am trying to test the optional view with the following:

    it("should show an error message when the button is pressed") {
            do {
                let view = MyView()
                let button = try view.inspect().vStack().button(0)
    
                try button.tap()
    
                let errorMessage = try view.inspect().vStack().text(1).string()
    
                expect(view.showErrorMessage).to(beTrue())
                expect(errorMessage).to(equal("ERROR"))
            } catch {
                fail("failure - \(error)")
            }
     }
    

    The test fails on both assertions, as the showErrorMessage property remains false and the optional Text view remains hidden. Additionally, showErrorMessage = false is printed to the console when try button.tap() is called.

    When I run the code in the UI simulator, it works as expected; the Text view is toggled in and out of visibility and the correct value of showErrorMessage is printed to the console.

    Is there something I'm missing that's causing the different behavior in the test versus the UI simulator?

    opened by shayna34 9
  • Is there a way to identify a view of any type in a SwiftUI view hierarchy given some constraint?

    Is there a way to identify a view of any type in a SwiftUI view hierarchy given some constraint?

    Hi there.

    So I know there exists a way to inspect the view hierarchy for a particular UI component by specifying the view hierarchy explicitly, such as try view.inspect().vStack().button(0). However, to avoid exposing implementation details of the view hierarchy in my tests, I'd instead like to determine if there exists a UI component within a view hierarchy that matches some constraint.

    For example, say there is a landing screen with a view hierarchy that contains a sign-in button along with other UI components. In my test, I'd like to verify that there exists a button with the text, "Sign In," rather than specifying the view hierarchy explicitly and thereby exposing implementation details.

    Is there any way to do this? Much appreciated.

    opened by SumoSimo 8
  • Can't inspect `accessibilityLabel()`

    Can't inspect `accessibilityLabel()`

    It appears that because the path for accessibilityLabel() uses elements|some, I always get a thrown error when trying to read it. In my inspection of the inspector (sorry!) I have the following modifier:

        ▿ modifier : AccessibilityAttachmentModifier
          ▿ attachment : Optional<AccessibilityAttachment>
            ▿ some : AccessibilityAttachment
              ▿ properties : AccessibilityProperties
                ▿ plist : [TypedValueKey = Optional(SwiftUI.AccessibilityValue(value: nil, description: Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("123456789012"), modifiers: [])))), LabelKey = Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("UPCA product code"), modifiers: [])), TraitsKey = AccessibilityTraitStorage(mask: SwiftUI.AccessibilityTraitStorage.(unknown context at $7fff2c6c4d64).TraitSet(rawValue: 288), values: SwiftUI.AccessibilityTraitStorage.(unknown context at $7fff2c6c4d64).TraitSet(rawValue: 288))]
                  ▿ elements : Optional<Element>
                    ▿ some : TypedValueKey = Optional(SwiftUI.AccessibilityValue(value: nil, description: Optional(SwiftUI.Text(storage: SwiftUI.Text.Storage.verbatim("123456789012"), modifiers: []))))
    

    As you can see, LabelKey is in plist, but does not appear in plist.elements.some. Only my accessibilityValue() is in elements. Since accessibilityValue() is also in plist, I think the appropriate fix is to simply chop off elements|some in the path for accessibilityElement(_::::)

    opened by nrivard 8
  • Problem with find and view with generics

    Problem with find and view with generics

    Before I get to the problem let me explain my motivation. The library I'm writing handles navigation, so the tests for it involve creating an absurd number of views that are unique to a given test. The problem is that find (and, for that matter, some other inspection methods) no longer work due to the regex check on type names.

    Here is a test that reproduces the problem:

    func testViewContainerWithNestedGenericParameter() throws {
        struct ViewWrapper<V: View>: View {
            @ViewBuilder var view: () -> V
            init(@ViewBuilder view: @escaping () -> V) {
                self.view = view
            }
            var body: some View {
                view()
            }
        }
        let sut = try ViewWrapper {
            ViewWrapper {
                Text("findme")
            }
        }.inspect()
        XCTAssertNoThrow(try sut.find(ViewWrapper<ViewWrapper<Text>>.self))
        XCTAssertNoThrow(try sut.find(ViewWrapper<Text>.self))
    }
    

    Here is the commit that caused the problem: https://github.com/nalexn/ViewInspector/commit/22a48cec7ca25851a79b500e91d70893f76ae462

    I've verified in a fork that reverting this commit gets me back to the old, functioning behavior.

    I have another problem with find and generics that likely has a similar cause, but I'm having trouble creating a minimally reproducible example.

    opened by Tyler-Keith-Thompson 5
  • Running tests on iPhone simulator results in crash

    Running tests on iPhone simulator results in crash

    I am developing using Xcode 14.1 on macOS 13.1. My Mac has an M1 processor. I opened the Package.swift and ran the tests (Cmd + u) for the iPhone 14 Pro simulator on the HEAD SHA of the master branch (ac7df67c4e0593470eda90a550f8493a81609745). I encounter a crash in TimelineViewTests.testEnclosedView().

    Screenshot 2022-12-18 at 5 18 12 PM Screenshot 2022-12-18 at 5 18 15 PM
    opened by bachand 0
  • View does not have 'onDisappear' modifier

    View does not have 'onDisappear' modifier

    Hi @nalexn

    I want test code which should call in onDisappear function. But for me isn't possible to call this func from the test.

    I try this here

    try sut.inspect().callOnDisappear()
    XCTAssertEqual(makeSUTResult.cameraSettingsDelegateSpy.storeCameraSettingsCount, 1)
    

    But I get always this error

    failed: caught error: "CameraView does not have 'onDisappear' modifier"

    I added the onDisappear to my view which I'm testing.

    How I can write a test for my use case?

    Thank you

    opened by bitsmakerde 0
  • Not finding Elements when a View is wrapped with NavigationStack

    Not finding Elements when a View is wrapped with NavigationStack

    I have a view which has a NavigationStack around it.

    var body: some View {
    return NavigationStack {
                VStack {
                    Text("Hello")
                    ..........
                }
    

    Now when trying to find the text "Hello" via (I use quick and nimble for testing, but XCTest gives the same result)

                    expect(try homeView.inspect()
                        .find(text: "Hello")
                    ).toNot(beNil())
    

    it gives me a failed test. When removing the NavigationStack the test succeeds. Also when adding a tag to the NavigationStack as .tag property and print the output of

    expect(try homeView.inspect()
                        .find(viewWithTag: "NavigationStack")
    

    (this test also succeeds)

    it gives me the whole hierarchy which clearly shows (SwiftUI.Text(storage: SwiftUI.Text.Storage.anyTextStorage(<LocalizedTextStorage: 0x00006000010656d0>: "Hello") at some point inside of it.

    Am I missing something or is there a bug when traversing through Views wrapped in a NavigationStack via ViewInspector?

    bug 
    opened by domfz 2
  • Question: why do views need to conform to `Inspectable` to be inspected?

    Question: why do views need to conform to `Inspectable` to be inspected?

    This is an amazing library. Wow!

    I am learning how this library works and I have a question about the Inspectable protocol.

    Let's say I have a toy SwiftUI view setup like this:

    import SwiftUI
    
    struct MyScreen: View {
      var body: some View {
        MyTextWrapperView(title: "Hello world")
      }
    }
    
    struct MyTextWrapperView: View {
      let title: String
    
      var body: some View {
        Text(title)
      }
    }
    

    And then let's say I want to verify that my screen shows the string "Hello world" somewhere at any level of the hierarchy. I'd write that test like this:

    import ViewInspector
    import XCTest
    
    final class MyScreenTests: XCTestCase {
      func test_verifyTextPresence() {
        XCTAssertNoThrow(try MyScreen().inspect().find(text: "Hello world"))
      }
    }
    

    I find that for this to to pass I need to conform both MyScreen and MyTextWrapperView to Inspectable.

    extension MyScreen: Inspectable { }
    extension MyTextWrapperView: Inspectable { }
    

    When building infrastructure for a large codebase it can be challenging to ensure that all of the expected views conform to Inspectable. I was hoping that ViewInspector's find(text:) would work out of the box without any view needing to be conformed to Inspectable. I don't mind needing to conform MyScreen to Inspectable; the part that is more difficult to reason about is the need to conform child view types, like MyTextWrapperView, used within the hierarchy.

    I am trying to understand the reason why the Inspectable conformance is necessary. I see that the Inspectable protocol has one property and one method, and that there's a default implementation for each when Self: View. However I haven't been able to find any documentation as to why the Inspectable protocol is necessary.

    Thanks again for filling this huge gap in the SwiftUI ecosystem which such a thoughtfully built library 😄

    help wanted 
    opened by bachand 6
Releases(0.9.3)
  • 0.9.3(Dec 25, 2022)

    Released on 2022-12-25.

    What's new:

    • Conformance to Inspectable protocol is no longer a requirement when inspecting custom views and modifiers. Kudos to @bachand for R&D of this feature (#216)

    • Fixes and PRs:

      • Added AttributedString support for Text (#202)
      • Fixed "cannot find type 'ToolbarPlacement' in scope" (#199, #206)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.2(Sep 17, 2022)

    Released on 2022-09-17.

    Requires Xcode 14.0

    What's new:

    • Adds support for SwiftUI 4.0

    • New APIs:

      • find(viewWithAccessibilityLabel: ) and find(viewWithAccessibilityIdentifier: ) (PR #168)
      • axes: Axis.Set and showsIndicators: Bool inspectable attributes on ScrollView
      • callOnSubmit inspection of the onSubmit modifier (PR #184)
      • primaryAction inspection support on Menu (#183)
      • Inspectable attributes on Rotation3D (PR #191)
      • Label(_:systemImage:) inspection (#176)
    • Fixes and PRs:

      • 'callOnChange' not recognizing Optional parameters (#163)
      • Error when accessing the keyboardType in TextField (#171)
      • Failure to find .background() when BackgroundView contains Color (#169)
      • Error 'Cannot find 'XCTFail' in scope' when integrating with CocoaPods (#145)
    Source code(tar.gz)
    Source code(zip)
  • 0.9.1(Dec 30, 2021)

    Released on 2021-12-30.

    Requires Xcode 13.2

    What's new:

    • Added inspection support for:

      • AsyncImage
      • Canvas
      • ControlGroup
      • LocationButton
      • SignInWithAppleButton
      • TimelineView
      • VideoPlayer
      • EllipticalGradient (PR #135)
      • ContainerRelativeShape
      • tint() (PR #139)
      • listItemTint() (PR #137)
      • overlayPreferenceValue() (#144)
      • backgroundPreferenceValue()
      • statusBarHidden()
    • New APIs:

      • isAbsent: Bool for checking if optional view is present. Requested in #129 and #136
      • classified() for erasing the type of the view to ViewType.ClassifiedView

    Fixes and PRs:

    • Fixed 'Cannot find 'XCTFail' in scope' when integrating with CocoaPods (#145)
    • Fixed actualView() not injecting EnvironmentObjects (issue #149, PR #150)
    • Fixed find().actualView() gets type mismatch error on a nested view type within a custom generic container (#138)

    Happy coming New Year!

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Sep 19, 2021)

    Released on 2021-09-19.

    Requires Xcode 13.0

    What's new:

    • Added watchOS support (#125)
    • Existing inspection code updated for iOS 15 (#128)
    • Added support for Toolbar
    • Added support for safeAreaInset overlay
    • Added support for confirmationDialog
    • Inspector.print(:) is made public for easier view debugging

    Breaking changes:

    Fixes and PRs:

    • navigationBarItems no longer block the inspection starting from iOS 15 (#29)
    • Fixed Inspector.print(:) cyclic recursion (#105)
    • Fixed "callOnChange is not found on _ConditionalContent" (#126)
    • Fixed Shape views being reported as blockers for search (#133)
    • Merged PR #132: Adds support for FullScreenCover
    • Merged PR #124: Include ToolBarItem in readiness.md
    Source code(tar.gz)
    Source code(zip)
  • 0.8.1(Jun 17, 2021)

    Released on 2021-06-17.

    • Fixed compilation issues on Xcode 12.4 (#116)
    • Fixed some tests not passing for iOS 13
    • Merged PR #118: Fixed a link in the guide
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Jun 15, 2021)

    Released on 2021-06-15.

    • Inspection of a custom ViewModifier is now fully aligned with a custom View inspection and is devoid of some shortcomings. Your existing tests won't break, but if you choose to migrate to the new one - please refer to the guide for updated snippets for the class Inspection. Kudos to @gili-labs for playing an essential role in the research and development of this feature.

    • A View that has applied custom ViewModifier now inherits view and environment modifiers applied inside that custom ViewModifier. See the example below:

    struct MyModifier: ViewModifier {
        func body(content: Self.Content) -> some View {
            content
               .padding(.top, 15)
        }
    }
    
    let view = Text("Hi").modifier(MyModifier())
    

    Before: Text would report it has no padding modifier applied. Now: Text reports the padding applied from inside the MyModifier.

    • Merged PR #112: Added support for Map view inspection (requested in #59).
    • Added support for MapAnnotation inspection.
    Source code(tar.gz)
    Source code(zip)
  • 0.7.7(May 24, 2021)

  • 0.7.6(May 23, 2021)

    Released on 2021-05-23.

    • Added support for inspecting Alert, ActionSheet and Sheet. Instructions are provided in the guide.

    • New func isResponsive() -> Bool for checking if the control is responsive to the user's touch input.

    • All controls that are disabled, hidden or with allowsHitTesting(false) now throw an exception on attempt to programmatically trigger a user interaction.

    • Many of the view modifiers that impose their effect on the enclosed hierarchy, such as hidden, are now associated with the inner views as well.

    • Merged PR #107: Disabled Button throws an exception on tap attempt.

    • Merged PR #109: Added API for selecting a date with DatePicker

    • Fixed #104

    Source code(tar.gz)
    Source code(zip)
Owner
Alexey Naumov
Designing software the ruthless way
Alexey Naumov
A Mac and iOS Playgrounds Unit Testing library based on Nimble.

Spry Spry is a Swift Playgrounds Unit Testing library based on Nimble. The best thing about Spry is that the API matches Nimble perfectly. Which means

Quick 327 Jul 24, 2022
Switchboard - easy and super light weight A/B testing for your mobile iPhone or android app. This mobile A/B testing framework allows you with minimal servers to run large amounts of mobile users.

Switchboard - easy A/B testing for your mobile app What it does Switchboard is a simple way to remote control your mobile application even after you'v

Keepsafe 287 Nov 19, 2022
SwiftCheck is a testing library that automatically generates random data for testing of program properties

SwiftCheck QuickCheck for Swift. For those already familiar with the Haskell library, check out the source. For everybody else, see the Tutorial Playg

TypeLift 1.4k Dec 21, 2022
Testing the UI without UI Testing, a Swift experiment.

UI tests without UI Testing experiment This repo is a small experiment to see if there's an "in-between" for testing iOS applications. More feature-le

Joe Masilotti 20 Sep 26, 2022
TestSchedulerDemo - Demonstration code for iOS Unit Tests and Asynchronous

TestSchedulerDemo This repository contains demonstration code for my Medium arti

Carsten Wenderdel 0 Mar 19, 2022
Marvel - Marvel Characters App using MVVM, and including unit tests

Marvel About The purpose of this project is to develop a app using MVVM, and inc

null 1 Mar 20, 2022
Detailed explanations and implementations of various maths concepts for writing high performance code/algorithms backed with Unit tests.

Detailed explanations and implementations of various maths concepts which can help software Engineers write high performance code/algorithms backed with Unit tests.

Mussa Charles 2 Sep 25, 2022
The XCTest Project, A Swift core library for providing unit test support

XCTest The XCTest library is designed to provide a common framework for writing unit tests in Swift, for Swift packages and applications. This version

Apple 1k Jan 4, 2023
Mockit is a Tasty mocking framework for unit tests in Swift 5.0

Mockit Introduction Mockit is a Tasty mocking framework for unit tests in Swift 5.0. It's at an early stage of development, but its current features a

Syed Sabir Salman-Al-Musawi 118 Oct 17, 2022
Trying to implement Unit Tests for @Binding properties in a ViewModel

BindingTester Trying to implement Unit Tests for @Binding properties in a ViewModel ViewModel to be tested class SheetViewModel: ObservableObject {

Raphael Guye 0 Oct 22, 2021
Library for unifying the approach to network mocking in iOS unit- & UI-tests.

TinkoffMockStrapping Example To run the example project, clone the repo, and run pod install from the Example directory first. Requirements Installati

Online financial ecosystem 22 Jan 3, 2023
Catching fatal errors in unit tests

Precondition Catching When running tests which hit fatal errors, often preconditions the built-in support with XCTest. One package which supports cach

Brennan Stehling 0 Nov 28, 2021
Write unit tests which test the layout of a view in multiple configurations

Overview This library enables you to write unit tests which test the layout of a view in multiple configurations. It tests the view with different dat

LinkedIn 565 Nov 16, 2022
XCTestExtensions is a Swift extension that provides convenient assertions for writing Unit Test.

XCTestExtensions Features XCTAssertEventually (that convenient assertions for writing Unit Test). Use "XCTAssertEventually", you can write asynchronou

shindyu 22 Dec 1, 2022
Snapshot view unit tests for iOS

iOSSnapshotTestCase (previously FBSnapshotTestCase) What it does A "snapshot test case" takes a configured UIView or CALayer and uses the necessary UI

Uber Open Source 1.7k Jan 4, 2023
Sample project for testing out focus in SwiftUI and iOS 15

This project was to test out different ways of enabling focus in a SwiftUI app.

null 3 Dec 21, 2021
SwiftUI & Scenekit hit testing example.

HitTestApp SwiftUI & Scenekit hit testing example. This example application is written for macOS, though with minor changes it should be able to be ch

Eric Freitas 8 Oct 27, 2022
Automatic testing of your Pull Requests on GitHub and BitBucket using Xcode Server. Keep your team productive and safe. Get up and running in minutes. @buildasaur

Buildasaur Automatic testing of your Pull Requests on GitHub and BitBucket using Xcode Server. Keep your team productive and safe. Get up and running

Buildasaurs 774 Dec 11, 2022
Implementing and testing In-App Purchases with StoreKit2 in Xcode 13, Swift 5.5 and iOS 15.

StoreHelper Demo Implementing and testing In-App Purchases with StoreKit2 in Xcode 13, Swift 5.5, iOS 15. See also In-App Purchases with Xcode 12 and

Russell Archer 192 Dec 17, 2022