nalexn / ViewInspector

Runtime introspection and unit testing of SwiftUI views
MIT License
2.09k stars 145 forks source link

Not able to test Charts framework elements/protocols #304

Open asifrazap opened 1 month ago

asifrazap commented 1 month ago

Hi Team,

I have added Charts framework in SwiftUI with iOS 16. Tried to test Chart element using Quick and Nimble. Example is given below I am not able to test ChartContent protocol from Charts framework. Please help ?

import Charts
import SwiftUI

struct Workout: Identifiable {
    let id = UUID()
    let day: String
    let minutes: Int
}

extension Workout {
    static let walkWorkout: [Workout] = [
        .init(day: NSLocalizedString("mon", comment: ""), minutes: 23),
        .init(day: "Tue", minutes: 35),
        .init(day: "Wed", minutes: 55),
        .init(day: "Thu", minutes: 30),
        .init(day: "Fri", minutes: 15),
        .init(day: "Sat", minutes: 65),
        .init(day: "Sun", minutes: 81)
    ]

    static let runWorkout: [Workout] = [
        .init(day: NSLocalizedString("mon", comment: ""), minutes: 16),
        .init(day: "Tue", minutes: 12),
        .init(day: "Wed", minutes: 55),
        .init(day: "Thu", minutes: 34),
        .init(day: "Fri", minutes: 22),
        .init(day: "Sat", minutes: 43),
        .init(day: "Sun", minutes: 90)
    ]
}

struct BarChartExample: View {
    var workoutData = [
        (workoutType: "Walk", data: Workout.walkWorkout),
        (workoutType: "Run", data: Workout.runWorkout)
    ]

    var body: some View {
        Chart {
            BarChartExampleContent(workoutData: workoutData)
        }
        .frame(width: 300, height: 400)
        .chartXAxis {
            AxisMarks { axis in
                AxisValueLabel(centered: true) {
                    Text("\(Workout.walkWorkout[axis.index].day)")
                }
            }
        }
    }
}

struct BarChartExampleContent: ChartContent {
    var workoutData: [(workoutType: String, data: [Workout])]

    var body: some ChartContent {
        ForEach(workoutData, id: \.workoutType) { element in
            ForEach(element.data) {
                BarMark(
                    x: .value("Day", $0.day),
                    y: .value("Workout(in minutes)", $0.minutes)
                )
            }
            .foregroundStyle(by: .value("Workout(type)", element.workoutType))
            .position(by: .value("Workout(type)", element.workoutType))
        }
    }
}

Written test cases as below: -

let workoutData = [
    (workoutType: "Walk", data: Workout.walkWorkout),
    (workoutType: "Run", data: Workout.runWorkout)
]

class BarChartExampleSpec: QuickSpec {
    override func spec() {
        var subject: BarChartExample?
        beforeEach {
            subject = BarChartExample()
            subject?.workoutData = workoutData
        }
        describe("BarChartExample") {
            context("To test Chart with it's child elements") {
                it("check Chart") {
                    let chart = try? subject.inspect().find(Chart<ChartContentStub>.self)
                    expect(chart) != nil
                }
            }
        }
    }
}

class BarChartExampleContentSpec: QuickSpec {
    override func spec() {
        var subject: BarChartExampleContent?
        beforeEach {
            subject = BarChartExampleContent(workoutData: workoutData)
            subject?.workoutData = workoutData
        }
        describe("BarChartExampleContent") {
            context("BarChartContent View") {
                it("check Chart content") {
                    let chart = try? subject.inspect().find(DVBarChartContent.self)     **// Getting error here - Referencing instance method 'inspect(function:)' on 'Optional' requires that 'BarChartExampleContent' conform to 'View'**
                    expect(chart) != nil
                }
            }
        }
    }
}

class ChartContentStub: ChartContent {
    var body: some ChartContent {
        ForEach(workoutData, id: \.workoutType) { element in
            ForEach(element.data) {
                BarMark(
                    x: .value("Day", $0.day),
                    y: .value("Workout(in minutes)", $0.minutes)
                )
            }
            .foregroundStyle(by: .value("Workout(type)", element.workoutType))
            .cornerRadius(10)
            .position(by: .value("Workout(type)", element.workoutType))
        }
    }
}

Getting below error while testing -

Screenshot 2024-05-25 at 8 04 14 AM

Could you please help how to test ChartContent ?