swiftlang / swift-testing

A modern, expressive testing package for Swift
Apache License 2.0
1.67k stars 68 forks source link

Add a type for complex input generation that performs exploratory testing #25

Open SeanROlszewski opened 11 months ago

SeanROlszewski commented 11 months ago

Description

In order to support testing patterns where randomized input generation is used to explore a program's state space (such as fuzz, differential, property-based, or mutation testing) we need to add some affordance to developers to specify the following:

1) The next value to pass into a @Test 2) A way to receive the prior value and test result as a result of executing @Test 3) A way to receive additional information, such as changes in code coverage, from the last @Test 4) A way to stop input generation based on some heuristic or predicate.

Having these 4 items would be the essential set of primitives to really streamline the creation of tools like QuickCheck, libFuzzer, etc. with Swift Testing.

Perhaps this could take the form of a protocol, potentially named TestCaseInputGenerator, that has the following shape:

protocol TestCaseInputGenerator {
    associatedtype AdditionalContext
    func shouldStop<InputType>(given priorRun: (InputType, TestResult)?, additionalContext: AdditionalContext?) -> Bool
    func nextValue<InputType>(given priorRun: (InputType, TestResult)?, additionalContext: AdditionalContext?) -> InputType
}

AdditionalContext can encapsulate data of interest to the test; previously tried values, their results, and other data like code coverage, crashes, etc.

I'm filing this issue rather quickly, in between tasks, so apologizes in advance for oversights on the type signatures and compilability of the proposed API. :)

samritchie commented 2 months ago

There doesn’t seem to be an awful lot of interest in this feature - let me know what I can do to help? I’ve been working (very slowly) on a hedgehog port. The types of things I’d be looking for are:

Most libraries also have a verbose option that logs out inputs for all tests run, this could potentially be recorded the same way as the current parameterised test runs, although the number of sub-tests is potentially large.

An alternative approach may be some sort of test interceptor or embedded runner that can receive, filter, modify & generate test events/issues within its test context - this is the workaround I ended up using for XCTest support. I don’t think this would be as well-integrated into swift-testing overall but would be more flexible and may be simpler to implement.