Parameterized testing with ‘any’ values

Does Swift Testing support using ‘any’ values as parameters like seen here for ‘any ChartDataPoint.Type’

    @Test(arguments: [
        (expectedBodyFatValues,
         HKQuantityTypeIdentifier.bodyFatPercentage,
         HKUnit.percent(),
         BodyFatPoint.self),
        (expectedActiveEnergyValues,
         HKQuantityTypeIdentifier.activeEnergyBurned,
         HKUnit.kilocalorie(),
         ActiveCaloriesPoint.self),
        (expectedBodyMassValues,
         HKQuantityTypeIdentifier.bodyMass,
         HKUnit.pound(),
         BodyMassPoint.self),
        (expectedBasalEnergyValues,
         HKQuantityTypeIdentifier.basalEnergyBurned,
         HKUnit.kilocalorie(),
         BasalEnergyPoint.self)
    ])
    func healthKitDataReading(
        expectedData: [Double],
        identifier: HKQuantityTypeIdentifier,
        unit: HKUnit,
        dataChartType: any ChartDataPoint.Type
    ) async throws {...}

Currently I can’t get this code to work, and see the error

… Conflicting arguments to generic parameter 'C' ('[([Double], HKQuantityTypeIdentifier, HKUnit, BodyFatPoint.Type)]' vs. '[([Double], HKQuantityTypeIdentifier, HKUnit, ActiveCaloriesPoint.Type)]' vs. '[([Double], HKQuantityTypeIdentifier, HKUnit, BodyMassPoint.Type)]' vs. '[([Double], HKQuantityTypeIdentifier, HKUnit, BasalEnergyPoint.Type)]')

Also, I can’t seem to use variables like ‘expectedBodyFatValues’ due to the error

Instance member 'expectedBodyFatValues' cannot be used on type 'Health_Mix_Swift_Tests'; did you mean to use a value of this type instead?

Only way I’ve found around this is including the entire array of values as the parameter, but it’s very cumbersome.

Answered by DTS Engineer in 797310022

Regarding your first issue, I boiled it down to this example:

import Testing

protocol ChartDataPoint { }
struct BodyFatPoint: ChartDataPoint { }
struct ActiveCaloriesPoint: ChartDataPoint { }

struct Tests {

    @Test(arguments: [
        ([1.0],
         BodyFatPoint.self as any ChartDataPoint.Type),
        ([1.0],
         ActiveCaloriesPoint.self as any ChartDataPoint.Type)
    ])
    func healthKitDataReading(
        expectedData: [Double],
        dataChartType: any ChartDataPoint.Type
    ) async throws { }
}

If you remove the two as any ChartDataPoint.Type coercions, it fails with the same error you’re seeing. AFAICT that’s because Swift Testing is expected all the array elements to have the same type, and for that to match the type of the function parameters, but that’s not the case without the coercions.

Regarding your second issue, can you tweak my boiled-down example to illustrate the problem.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

Regarding your first issue, I boiled it down to this example:

import Testing

protocol ChartDataPoint { }
struct BodyFatPoint: ChartDataPoint { }
struct ActiveCaloriesPoint: ChartDataPoint { }

struct Tests {

    @Test(arguments: [
        ([1.0],
         BodyFatPoint.self as any ChartDataPoint.Type),
        ([1.0],
         ActiveCaloriesPoint.self as any ChartDataPoint.Type)
    ])
    func healthKitDataReading(
        expectedData: [Double],
        dataChartType: any ChartDataPoint.Type
    ) async throws { }
}

If you remove the two as any ChartDataPoint.Type coercions, it fails with the same error you’re seeing. AFAICT that’s because Swift Testing is expected all the array elements to have the same type, and for that to match the type of the function parameters, but that’s not the case without the coercions.

Regarding your second issue, can you tweak my boiled-down example to illustrate the problem.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

@DTS Engineer Thanks for explaining what was wrong with the first part!

import Testing

protocol ChartDataPoint { }
struct BodyFatPoint: ChartDataPoint { }
struct ActiveCaloriesPoint: ChartDataPoint { }

struct Tests {

    let expectedBodyFatValues: [Double] = [1.0, 2.0, 3.0]
    let expectedActiveEnergyValues: [Double] = [1.0, 2.0, 3.0]

    @Test(arguments: [
        (expectedBodyFatValues,
         BodyFatPoint.self as any ChartDataPoint.Type),
        (expectedActiveEnergyValues,
         ActiveCaloriesPoint.self as any ChartDataPoint.Type)
    ])
    func healthKitDataReading(
        expectedData: [Double],
        dataChartType: any ChartDataPoint.Type
    ) async throws { }
}

I want to use a variable in place of the double array. I’m using 365 values, and with 4 tuples of parameters, it’s a lot to see in the parameters.

Oh, I see what you’re getting at here. Thanks for the clarification.

The issue is that the argument values are being evaluated in the context of the Tests type, not an instance of that type. So, you can make this code work by changing the two constants from let to static let.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Parameterized testing with ‘any’ values
 
 
Q