SwiftCheck is a testing library that automatically generates random data for testing of program properties

Overview

Carthage compatible Build Status Gitter chat

SwiftCheck

QuickCheck for Swift.

For those already familiar with the Haskell library, check out the source. For everybody else, see the Tutorial Playground for a beginner-level introduction to the major concepts and use-cases of this library.

Introduction

SwiftCheck is a testing library that automatically generates random data for testing of program properties. A property is a particular facet of an algorithm or data structure that must be invariant under a given set of input data, basically an XCTAssert on steroids. Where before all we could do was define methods prefixed by test and assert, SwiftCheck allows program properties and tests to be treated like data.

To define a program property the forAll quantifier is used with a type signature like (A, B, C, ... Z) -> Testable where A : Arbitrary, B : Arbitrary ... Z : Arbitrary. SwiftCheck implements the Arbitrary protocol for most Swift Standard Library types and implements the Testable protocol for Bool and several other related types. For example, if we wanted to test the property that every Integer is equal to itself, we would express it as such:

func testAll() {
    // 'property' notation allows us to name our tests.  This becomes important
    // when they fail and SwiftCheck reports it in the console.
    property("Integer Equality is Reflexive") <- forAll { (i : Int) in
        return i == i
    }
}

For a less contrived example, here is a program property that tests whether Array identity holds under double reversal:

` is the labelling operator allowing us to name each sub-part // in output generated by SwiftCheck. For example, this property reports: // // *** Passed 100 tests // (100% , Right identity, Left identity) return (xs.reversed().reversed() == xs) "Left identity" ^&&^ (xs == xs.reversed().reversed()) "Right identity" } ">
property("The reverse of the reverse of an array is that array") <- forAll { (xs : [Int]) in
    // This property is using a number of SwiftCheck's more interesting 
    // features.  `^&&^` is the conjunction operator for properties that turns
    // both properties into a larger property that only holds when both sub-properties
    // hold.  `` is the labelling operator allowing us to name each sub-part
    // in output generated by SwiftCheck.  For example, this property reports:
    //
    // *** Passed 100 tests
    // (100% , Right identity, Left identity)
    return
        (xs.reversed().reversed() == xs)  "Left identity"
        ^&&^
        (xs == xs.reversed().reversed())  "Right identity"
}

Because SwiftCheck doesn't require tests to return Bool, just Testable, we can produce tests for complex properties with ease:

` to define a precondition for // this test. If the precondition fails the test is discarded. If it holds // the test proceeds. return (!l.isEmpty && l != [0]) ==> { let ls = self.shrinkArbitrary(l) return (ls.filter({ $0 == [] || $0 == [0] }).count >= 1) } } ">
property("Shrunken lists of integers always contain [] or [0]") <- forAll { (l : [Int]) in
    // Here we use the Implication Operator `==>` to define a precondition for
    // this test.  If the precondition fails the test is discarded.  If it holds
    // the test proceeds.
    return (!l.isEmpty && l != [0]) ==> {
        let ls = self.shrinkArbitrary(l)
        return (ls.filter({ $0 == [] || $0 == [0] }).count >= 1)
    }
}

Properties can even depend on other properties:

property("Gen.one(of:) multiple generators picks only given generators") <- forAll { (n1 : Int, n2 : Int) in
    let g1 = Gen.pure(n1)
    let g2 = Gen.pure(n2)
    // Here we give `forAll` an explicit generator.  Before SwiftCheck was using
    // the types of variables involved in the property to create an implicit
    // Generator behind the scenes.
    return forAll(Gen.one(of: [g1, g2])) { $0 == n1 || $0 == n2 }
}

All you have to figure out is what to test. SwiftCheck will handle the rest.

Shrinking

What makes QuickCheck unique is the notion of shrinking test cases. When fuzz testing with arbitrary data, rather than simply halt on a failing test, SwiftCheck will begin whittling the data that causes the test to fail down to a minimal counterexample.

For example, the following function uses the Sieve of Eratosthenes to generate a list of primes less than some n:

/// The Sieve of Eratosthenes:
///
/// To find all the prime numbers less than or equal to a given integer n:
///    - let l = [2...n]
///    - let p = 2
///    - for i in [(2 * p) through n by p] {
///          mark l[i]
///      }
///    - Remaining indices of unmarked numbers are primes
func sieve(_ n : Int) -> [Int] {
    if n <= 1 {
        return []
    }

    var marked : [Bool] = (0...n).map { _ in false }
    marked[0] = true
    marked[1] = true

    for p in 2..<n {
        for i in stride(from: 2 * p, to: n, by: p) {
            marked[i] = true
        }
    }

    var primes : [Int] = []
    for (t, i) in zip(marked, 0...n) {
        if !t {
            primes.append(i)
        }
    }
    return primes
}

/// Short and sweet check if a number is prime by enumerating from 2...⌈√(x)⌉ and checking 
/// for a nonzero modulus.
func isPrime(n : Int) -> Bool {
    if n == 0 || n == 1 {
        return false
    } else if n == 2 {
        return true
    }
    
    let max = Int(ceil(sqrt(Double(n))))
    for i in 2...max {
        if n % i == 0 {
            return false
        }
    }
    return true
}

We would like to test whether our sieve works properly, so we run it through SwiftCheck with the following property:

import SwiftCheck

property("All Prime") <- forAll { (n : Int) in
    return sieve(n).filter(isPrime) == sieve(n)
}

Which produces the following in our testing log:

Test Case '-[SwiftCheckTests.PrimeSpec testAll]' started.
*** Failed! Falsifiable (after 10 tests):
4

Indicating that our sieve has failed on the input number 4. A quick look back at the comments describing the sieve reveals the mistake immediately:

- for i in stride(from: 2 * p, to: n, by: p) {
+ for i in stride(from: 2 * p, through: n, by: p) {

Running SwiftCheck again reports a successful sieve of all 100 random cases:

*** Passed 100 tests

Custom Types

SwiftCheck implements random generation for most of the types in the Swift Standard Library. Any custom types that wish to take part in testing must conform to the included Arbitrary protocol. For the majority of types, this means providing a custom means of generating random data and shrinking down to an empty array.

For example:

{ return Gen<(Int, Int)>.zip(Int.arbitrary, Int.arbitrary).map(ArbitraryFoo.init) } } class SimpleSpec : XCTestCase { func testAll() { property("ArbitraryFoo Properties are Reflexive") <- forAll { (i : ArbitraryFoo) in return i.x == i.x && i.y == i.y } } } ">
import SwiftCheck
 
public struct ArbitraryFoo {
    let x : Int
    let y : Int

    public var description : String {
        return "Arbitrary Foo!"
    }
}

extension ArbitraryFoo : Arbitrary {
    public static var arbitrary : Gen {
        return Gen<(Int, Int)>.zip(Int.arbitrary, Int.arbitrary).map(ArbitraryFoo.init)
    }
}

class SimpleSpec : XCTestCase {
    func testAll() {
        property("ArbitraryFoo Properties are Reflexive") <- forAll { (i : ArbitraryFoo) in
            return i.x == i.x && i.y == i.y
        }
    }
}

There's also a Gen.compose method which allows you to procedurally compose values from multiple generators to construct instances of a type:

public static var arbitrary : Gen {
    return Gen<MyClass>.compose { c in
        return MyClass(
            // Use the nullary method to get an `arbitrary` value.
            a: c.generate(),

            // or pass a custom generator
            b: c.generate(Bool.suchThat { $0 == false }),

            // .. and so on, for as many values and types as you need.
            c: c.generate(), ...
        )
    }
}

Gen.compose can also be used with types that can only be customized with setters:

public struct ArbitraryMutableFoo : Arbitrary {
    var a: Int8
    var b: Int16
    
    public init() {
        a = 0
        b = 0
    }
    
    public static var arbitrary: Gen {
        return Gen.compose { c in
            var foo = ArbitraryMutableFoo()
            foo.a = c.generate()
            foo.b = c.generate()
            return foo
        }
    }
}

For everything else, SwiftCheck defines a number of combinators to make working with custom generators as simple as possible:

.choose((0, 15)) let uppers = Gen.fromElements(in: "A"..."Z") let lowers = Gen.fromElements(in: "a"..."z") let numbers = Gen.fromElements(in: "0"..."9") /// This generator will generate `.none` 1/4 of the time and an arbitrary /// `.some` 3/4 of the time let weightedOptionals = Gen.frequency([ (1, Gen.pure(nil)), (3, Int.arbitrary.map(Optional.some)) ]) ">
let onlyEven = Int.arbitrary.suchThat { $0 % 2 == 0 }

let vowels = Gen.fromElements(of: [ "A", "E", "I", "O", "U" ])

let randomHexValue = Gen<UInt>.choose((0, 15))

let uppers = Gen<Character>.fromElements(in: "A"..."Z")
let lowers = Gen<Character>.fromElements(in: "a"..."z")
let numbers = Gen<Character>.fromElements(in: "0"..."9")
 
/// This generator will generate `.none` 1/4 of the time and an arbitrary
/// `.some` 3/4 of the time
let weightedOptionals = Gen<Int?>.frequency([
    (1, Gen<Int?>.pure(nil)),
    (3, Int.arbitrary.map(Optional.some))
])

For instances of many complex or "real world" generators, see ComplexSpec.swift.

System Requirements

SwiftCheck supports OS X 10.9+ and iOS 7.0+.

Setup

SwiftCheck can be included one of two ways:

Using The Swift Package Manager

  • Add SwiftCheck to your Package.swift file's dependencies section:
.package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.8.1")

Using Carthage

  • Add SwiftCheck to your Cartfile
  • Run carthage update
  • Drag the relevant copy of SwiftCheck into your project.
  • Expand the Link Binary With Libraries phase
  • Click the + and add SwiftCheck
  • Click the + at the top left corner to add a Copy Files build phase
  • Set the directory to Frameworks
  • Click the + and add SwiftCheck

Using CocoaPods

  • Add our Pod to your podfile.
  • Run $ pod install in your project directory.

Framework

  • Drag SwiftCheck.xcodeproj into your project tree as a subproject
  • Under your project's Build Phases, expand Target Dependencies
  • Click the + and add SwiftCheck
  • Expand the Link Binary With Libraries phase
  • Click the + and add SwiftCheck
  • Click the + at the top left corner to add a Copy Files build phase
  • Set the directory to Frameworks
  • Click the + and add SwiftCheck

License

SwiftCheck is released under the MIT license.

Comments
  • Gen returning values outside of range

    Gen returning values outside of range

    I have a short test that generates email addresses, and then feeds them to our UI. The UI then validates them against certain rules.

    The problem is, the generators seem to be generating invalid data sets (meaning, Strings that they should not be generating). The rules do not include, for example, spaces and control characters. They also specify that the email should always contain a name, hostname, @, and a TLD.

    But the tests generate data that fails on the following types of values:

    09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '7_B.dt'
    09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '[email protected]'
    09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '[email protected]'
    09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '.dA'
    09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '.d
    '
    

    Here's the test case:

    func wrap3(l : String) -> String -> String -> String -> String -> String {
        return { m in { m2 in { m3 in { r in l + m + m2 + m3 + r } } } }
    }
    
    fit("handle randomly generated addresses with Unicode") {
        let upperCase : Gen<Character>= Gen<Character>.fromElementsIn("A"..."Z")
        let lowerCase : Gen<Character> = Gen<Character>.fromElementsIn("a"..."z")
        let numeric : Gen<Character> = Gen<Character>.fromElementsIn("0"..."9")
        let alpha: Gen<Character> = Gen<Character>.oneOf([upperCase, lowerCase])
        let alphanumeric: Gen<Character> = Gen<Character>.oneOf([alpha, numeric])
        let latin: Gen<Character> = Gen<Character>.fromElementsIn("\u{00C0}"..."\u{02B8}")
        let allowedSymbols = Gen<Character>.fromElementsOf(["%", "+", "-", "_", "."])
        let special = Gen<Character>.fromElementsOf(["!", "#", "$", "%", "&", "'", "*", "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~", "."])
    
        let localEmail = Gen<Character>.oneOf([
            alphanumeric,
            allowedSymbols
            ]).proliferateNonEmpty().suchThat({ $0.count > 2 && $0.count < 25 }).fmap(String.init)
    
        let hostname = Gen<Character>.oneOf([
            alpha
            ]).proliferateNonEmpty().suchThat({ $0.count < 10 }).fmap(String.init)
    
        let tld = lower.proliferateNonEmpty().suchThat({ $0.count > 1 && $0.count < 4 }).fmap(String.init)
    
        let emailGen = wrap3 <^> localEmail <*> Gen.pure("@") <*> hostname <*> Gen.pure(".") <*> tld
    
        property("Unicode email addresses validate correctly") <- forAll(emailGen) { (e : String) in
            logger.info("generated '\(e)'")
    
                f.text = e
            return f.isValid
        }
    }
    
    opened by buildreactive 26
  • Documentarian

    Documentarian

    I'm using this pull request as an opportunity to try and quash a lot more of the ambiguity around using this framework in a meaningful way by providing a large amount of documentation. As such, I'm giving @zbeckman the authority to green light this.

    I'd appreciate if you would use this issue to comment on any existing documentation and provide feedback about any new stuff that was provided. As a new user your experience with this library so far is probably the most valuable thing I could have at my disposal for this pull request.

    opened by CodaFi 17
  • allow specifying seed so that `generate` can be repeatable.

    allow specifying seed so that `generate` can be repeatable.

    I have a situation where the length of an array that I need to generate will be determined dynamically by the code under test. I was attempting to use the variant method, passing it an arbitrary Int which is an input to my test case to reliably and deterministically set the value passed to .proliferate. Upon closer inspection, I see that variant doesn't actually reset the seed by rather just mixes it with a pre-existing seed which is out of my control. Is there any way to either set the original seed or add a new method, perhaps called say resetSeed, which would allow this?

    opened by johanatan 14
  • Generate Mirrors

    Generate Mirrors

    Adds an arbitrary instance for Mirror.

    Unfortunately the instance is very heavy right now due to type checker issues. I want to see what this does to CI compile times before I do anything crazy.

    opened by CodaFi 14
  • Final Updates

    Final Updates

    In order to make the merge into master this branch begins a feature freeze on swift-develop. The last thing I will be adding for now is the ability to use Cocoapods with 2.0.

    opened by CodaFi 14
  • Wanted: Some way to reproduce results

    Wanted: Some way to reproduce results

    I just had a test failure (where I couldn't see the counterexample, see #115), but I can't reproduce it. It would be great if test failures would print the seed and if there was a trivial way of plugging that seed back into the test to reproduce that case.

    bug enhancement help wanted 
    opened by lilyball 13
  • Gen.chooseNum() aka proliferateSized(min, max)

    Gen.chooseNum() aka proliferateSized(min, max)

    It would be great to have an ability to build generators that generate a specific (but random) range. For example, taking from my Scala code:

    def unicodeGenerator(generator: Gen[Char] = Gen.alphaChar, minimum: Int = 5, maximum: Int = 20): Gen[String] = Gen.chooseNum(minimum, maximum).flatMap { n =>
        Gen.buildableOfN[String, Char](n, generator)
    }
    

    The above generates a random series of Unicode between 5 and 20 characters. Another expression that's pretty useful:

    val shortCodeGenerator: Gen[String] = Gen.chooseNum(3, 10).flatMap { n =>
        Gen.sequence[String, Char](List.fill(n)(Gen.alphaNumChar))
    }
    

    In this latter example, I'm creating an alphanumeric code that is 3 to 10 characters in length.

    It would be very handy to be able to specify a range as in proliferateSized(min, max).

    opened by buildreactive 13
  • Report on which sub-property failed

    Report on which sub-property failed

    I may be doing something wrong, but when I use sub properties and one of them fails, all I get in the output are the inputs used and the name of the parent property. For example:

    property("equtatable") <- forAll {
        (a: Int, b: Int) in
    
        let reflexive = EquatableTestUtilities.checkReflexive { Constant(a) } <?> "reflexive"
        let symmetric = EquatableTestUtilities.checkSymmetric { Constant(a) } <?> "symmetric"
        let transitive = EquatableTestUtilities.checkTransitive { Constant(a) } <?> "transitive"
        let notEquate = (a != b) ==> {
            EquatableTestUtilities.checkNotEquate(
                { Constant(b) },
                { Constant(b) }
            )
        }() <?> "not-equate"
    
        return reflexive ^&&^ symmetric ^&&^ transitive ^&&^ notEquate
    }
    

    will just say that "equatable" failed, instead of also telling me that "not-equate" failed:

    *** Failed! Proposition: equtatable
    Falsifiable (after 1 test):
    0
    1
    *** Passed 0 tests
    

    If I get it to go past 1 test, it looks even weirder, saying 100% of the sub-properties passed?:

    *** Failed! Proposition: equtatable
    Falsifiable (after 2 tests and 2 shrinks):
    -2
    0
    *** Passed 1 tests
    (100%, not-equate, transitive, symmetric, reflexive)
    

    Basically, I feel it would be really valuable to know which sub-properties have failed. Sometimes custom arbitrary types are expensive to compute and it's easier to do many sub-properties off the same random values.

    opened by brentleyjones 12
  • Code generation with GYB

    Code generation with GYB

    What's in this pull request?

    Generates definitions of zip and map for arities up to 22 (configurable in .gyb template with max_arity).

    Why merge this pull request?

    Allows changing the arity of map and zip functions without duplicating any code. The primitive footprint for the API is also reduced with all zip functions based on zip(_:_:). There are now nine fewer functions that call the low level Gen(unGen:) initializer.

    What's worth discussing about this pull request?

    We might want other derived functions like ap generated to max_arity number of definitions.

    What downsides are there to merging this pull request?

    The downside of generated code, which is that it's mechanized instead of an abstraction through code. It's harder to develop a template than regular code since there's a level of indirection when debugging.


    This change is Reviewable

    opened by adamkuipers 10
  • ComplexSpec.swift testIPv6Properties generates identical examples for a test run

    ComplexSpec.swift testIPv6Properties generates identical examples for a test run

    The e-mail example "testEmailAddressProperties()" in the same file works fine.

    The IP example "testIPv6Properties()" will generate 100 examples of the same input for each test run, this can be checked by adding a simple print.

    opened by MichaelShaw 10
  • CheckerArgument for minTests

    CheckerArgument for minTests

    Is it possible to use CheckerArguments to specify a minimum number of positive tests? I've been playing with it, e.g,

    CheckerArguments(replay: Optional.Some((standardRNG, 10)), maxAllowableSuccessfulTests: 200, maxAllowableDiscardedTests: 100, maxTestCaseSize: 1000)
    

    It seems we have control of the maximum parameters, but not minimum.

    But the most common need we have is to increase the number of tests from 100. For example, we have some patterns that require a pretty wide range of testing before we can be "reasonably certain" that all conditions are passed. We'd like to be able to specify 1,000 test cases for these.

    Other situations in which having more than 100 passing test cases make sense:

    1. Performance. Quite often we need to run more than 100 cases in order to obtain a reasonable baseline on performance checking.
    2. Volume oriented testing. For instance, does the database create 10,000 records without failing?
    3. Large data sets. Already mentioned: Situations where failure is only detectable on the order of 1 in a 1,000 cases.
    opened by buildreactive 10
  • Fix usages in README and doc comment

    Fix usages in README and doc comment

    What's in this pull request?

    Fixes a usage shown in README: Bool.suchThat is mentioned, but it doesn't exist.

    Why merge this pull request?

    It makes the documentation a bit more c/p friendly.

    What's worth discussing about this pull request?

    :man_shrugging:

    What downsides are there to merging this pull request?

    :man_shrugging:

    opened by Trundle 0
  • (U)Int `arbitrary` implementation provides limited values with `.proliferate(withSize:)`

    (U)Int `arbitrary` implementation provides limited values with `.proliferate(withSize:)`

    Version

    077c096c3ddfc38db223ac8e525ad16ffb987138

    Environment

    • macOS 11.6
    • Xcode 13.1RC
    • 2019 Intel 16" MBP

    Description

    When combined with proliferate(withSize:), (U)Int[8|16|32|64].arbitrary will only generate values from -30 to 30 if signed, and 0 to 30 if unsigned.

    Steps To Reproduce

    Set(UInt.arbitrary.proliferate(withSize: 2000).generate).sorted()

    Expected Result

    Random values in a wider range

    Actual Result

    Very limited range of random values

    Additional information

    None

    opened by nesevis 0
  • Unable to run Playground

    Unable to run Playground

    Version

    abeeb22

    Environment

    Mac OS Catalina 10.15.7 (19H2) , XCode 12.3 (12C33)

    Description

    Playground does not work: No such module 'SwiftCheck'

    error: Tutorial.playground:64:16: error: cannot find 'Gen' in scope
    let onlyFive = Gen.pure(5)
                   ^~~
    
    error: Tutorial.playground:74:21: error: cannot find 'Gen' in scope
    let fromOnetoFive = Gen<Int>.fromElements(in: 1...5)
                        ^~~
    ....
    

    Steps To Reproduce

    1. git clone
    2. Open workspace
    3. Build -> succeeds
    4. Run playground-> Fails

    image

    opened by teameh 1
  • Xcode 12 or swift 5.3 incompatibility

    Xcode 12 or swift 5.3 incompatibility

    Version

    0.12.0

    Environment

    Mac OS X

    Description

    we are using swift check version 0.12.0 which can be used by frameworks which have been built by xcode 11. Now we are migrating to xcode 12.1 and if we use carthage to download swiftcheck it throws the below error

    Incompatible Swift version - framework was built with 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15) and the local version is 5.3 (swiftlang-1200.0.29.2 clang-1200.0.30.1).

    Is there any plans to use swift version 5.3 and compile swift check framework with it

    What do we need to do incase we use 0.12.0 and have to make it work with xcode 12.1

    Steps To Reproduce

    carthage update output

    Incompatible Swift version - framework was built with 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15) and the local version is 5.3 (swiftlang-1200.0.29.2 clang-1200.0.30.1).

    Expected Result

    Should have been able to build the framework

    Actual Result

    Incompatible Swift version - framework was built with 5.1.3 (swiftlang-1100.0.282.1 clang-1100.0.33.15) and the local version is 5.3 (swiftlang-1200.0.29.2 clang-1200.0.30.1).

    opened by rkhandelwal17 0
  • Integer partition generator

    Integer partition generator

    I've written an integer partition generator. For example, I have used it to write a generator for a tree:

    class Tree<A> {
        let root: A
        let subForest: [Tree<A>]
    }
    

    Let me know if you consider that this belongs to SwiftCheck, I'll then open a PR.

    opened by ferranpujolcamins 0
Releases(0.12.0)
  • 0.12.0(Mar 28, 2019)

    ⚠️ Breaking Changes Ahead ⚠️

    • Update to Swift 5, 5-style package format, and upgrade the Xcode project to compile in Swift 5 mode
    • Fix a bug where covered properties yielded a false positive if the condition was never true

    In the coming release, we will be reworking the coverage system to better match QuickCheck's current functionality.

    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Sep 19, 2018)

    ⚠️ Breaking Changes Ahead ⚠️

    • Update to the Swift 4.2 tooling, 4.2-style package format, and upgrade the Xcode project to compile in the 4.2 mode of the Swift compiler.
    • Removed the previously-deprecated Gen.map forms. Please use the corresponding Gen.zip function call itself
    • RemovedquickCheck(_ :name:). Use one of quickCheck(asserting:) or quickCheck(reporting:) instead.
    Source code(tar.gz)
    Source code(zip)
  • 0.10.1(Sep 19, 2018)

    • Remove self-referential Arbitrary instances for lazy collections
    • Updates to documentation

    ⚠️ Breaking Changes Ahead ⚠️

    This is the last release of SwiftCheck that supports Xcode 9 and its tooling.

    Source code(tar.gz)
    Source code(zip)
  • 0.10.0(Apr 12, 2018)

    Swift turns 4.1, and so SwiftCheck marches onward. This release brought with it a raft of improvements:

    • Non-operator spellings of the standard property() <- test pattern is provided in the form of the new quickCheck functions.
    • Conditional conformances for a raft of Standard Library types obviate most modifier types. As such, ArrayOf<T>, SetOf<T>, DictionaryOf<K, V>, and OptionalOf<T> have been removed. You can now directly use the natural types [T], Set<T>, [K: V], and T? respectively.
    • Documentation improvements and simplifications
    Source code(tar.gz)
    Source code(zip)
  • 0.9.1(Nov 2, 2017)

  • 0.9.0(Oct 20, 2017)

    SwiftCheck has internally upgraded to Swift 4.x.

    ⚠️ Breaking Changes Ahead ⚠️

    • Generation of floating point values specified an incorrect mask which could lead to decreased diversity of test values. Calculation of the mask has been corrected (h/t @sebastiangrail). Seeds replaying tests involving floating point numbers may need to be recalculated.

    • Gen.fromElements(in:), Gen.fromElements(of:), Gen.choose(_:) and Gen.chooseAny() have been updated to take fuller advantage of type inference and may no longer require explicit specialization. In addition, incorrect specializations may now be diagnosed as errors.

    • Gen.map(...) overloads have been deprecated and renamed Gen.zipWith. These have been given a similar overhaul to the above.

    Source code(tar.gz)
    Source code(zip)
  • 0.8.1(Sep 20, 2017)

    ⚠️ Breaking Changes Ahead ⚠️

    SwiftCheck now targets Xcode 9 and Swift 3.2. This is the last version of SwiftCheck that will support Swift 3.

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Mar 29, 2017)

  • 0.7.3(Feb 8, 2017)

    Fixes an issue where generating with the range (Int.max - 512, Int.max) could cause Builtin integer conversions to fail in the middle of random number generation.

    Source code(tar.gz)
    Source code(zip)
  • 0.7.2(Dec 1, 2016)

    Fixes an issue where extending the lifetime of ArrowOf<T, U> or IsoOf<T, U> could cause the underlying data table to be prematurely deallocated. (h/t @broomburgo)

    Source code(tar.gz)
    Source code(zip)
  • 0.7.1(Oct 9, 2016)

    • Fixes a compatibility issue with the Swift Package Manager
    • Thanks to gyb, zip and map now extend all the way out to arity 22.
    • The default RawRepresentable arbitrary instance has been removed.
    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Sep 16, 2016)

    ⚠️ Breaking Changes Ahead ⚠️

    SwiftCheck now fully supports Swift 3.0 and Xcode 8.0. That entails a bit of breakage:

    • The operator equivalents for functional combinators have been removed - these are, <^>, <*>, and >>-. Please use their nominal equivalents .map, .ap, and .flatMap respectively.
    • For long Applicative-style chains a <^> b <*> c <*> ... please use Gen.compose.
    • The little-known and barely-used exhaustive requirement of Arbitrary has been removed. Please use .once and .again.

    In addition, we have a number of framework improvements:

    • Gen.withBounds and Gen.chooseAny have been added to make interacting with RandomType and LatticeType generators even easier.
    • Fixes a potential crash when interacting with randomInRange for large unsigned values.
    • Documentation coverage and robustness continues to increase with the addition of doc comments.
    • SwiftCheck now builds without dependencies.

    Thank you to everybody who contributed to this release.

    @griotspeak @bgerstle @kouky @EuAndreh @kballard @gfontenot @kykim

    ❤️

    Source code(tar.gz)
    Source code(zip)
  • v0.6.2(Aug 29, 2016)

    This release includes support for Swift 2.3. In addition, we've completely revamped the way you can interact with Gen with a new combinator Gen.compose.

    Most users of this framework have to generate structures that involve significant amounts of setup and state. Previously, this meant you would have to go through the Applicative generator dance much like this

    MyClass.create
      <^> Int.arbitrary
      <*> String.arbitrary
      <*> Float.arbitrary
      <*> // ...
    

    This pattern, while terse, can lead to code bloat, bad compile times, and an overall bad experience with the framework. Instead, let's rewrite this in a more natural way [and infer all the types as a bonus!]

    Gen<MyClass>.compose { c in
        return MyClass(
            // Use the nullary method to get an `arbitrary` value.
            a: c.generate(),
    
           // or pass a custom generator
           b: c.generate(Bool.suchThat { $0 == false }),
    
           // .. and so on, for as many values and types as you need.
           c: c.generate(), ...
        )
    }
    

    We're going to begin the process of deprecating the old pattern and replacing it with Gen.compose.

    Source code(tar.gz)
    Source code(zip)
  • v0.6.1(May 6, 2016)

    This is an exciting release for us, led by a ton of helpful new features and patterns to make the framework easier more Swifty. Let's get right to it:

    • We know it can be a huge pain to work with the Applicative operators. To that end, we have included an instance of Monoidal Functors (rather than the old Applicative one) for Gen that provides an overload of zip up to 10 parameters. This means code that previously looked like this:
    public struct ArbitraryFoo {
        let x : Int
        let y : Int
    
        public static func create(x : Int) -> Int -> ArbitraryFoo {
            return { y in ArbitraryFoo(x: x, y: y) }
        }
    }
    
    extension ArbitraryFoo : Arbitrary {
        public static var arbitrary : Gen<ArbitraryFoo> {
            return ArbitraryFoo.create <^> Int.arbitrary <*> Int.arbitrary
        }
    }
    

    Can instead scrap the horrific initializer boilerplate and move straight to zip then map like so:

    public struct ArbitraryFoo {
        let x : Int
        let y : Int
    }
    
    extension ArbitraryFoo : Arbitrary {
        public static var arbitrary : Gen<ArbitraryFoo> {
            return Gen<(Int, Int)>.zip(Int.arbitrary, Int.arbitrary).map(ArbitraryFoo.init)
        }
    }
    

    If you ever run out of parameters, try flatMaping generators together.

    • Enums that conform to RawRepresentable now have a default instance of Arbitrary if you ask for it. To do so, simply declare your conformance to both RawRepresentable and Arbitrary and we'll handle the rest (thanks @bgerstle!).
    • zip and ap now have much, much faster implementations.
    • Fixed a bug (h/t @bgerstle) where replay seeds and sizes would fail to print if the test failed through coverage.
    • The framework's overall documentation and code coverage continue to improve.
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Mar 22, 2016)

    • SwiftCheck now fully supports Swift 2.2 and all that entails including
      • Support for the Swift Package Manager
      • Support for building SwiftCheck on Linux
    • The order of Gen.sequence and Rose.sequence have been fixed.
    • Documentation has been added to more parts of the framework.
    • Our own test suite is now more robust.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.2(Mar 17, 2016)

    • Adjusts internal RNG code for integers so it isn't subject to integer overflow conditions. This fixes #151 and may, as a side-effect, cause existing seed values created before this release to report different values as the calculations have become more robust.
    Source code(tar.gz)
    Source code(zip)
  • v0.5.1(Jan 22, 2016)

  • v0.5.0(Jan 8, 2016)

    :warning: Breaking Changes Ahead :warning:

    For SwiftCheck's 5th birthday, we sped up all the insides and moved the furniture around a bit.

    • The insides of Gen move an order of magnitude quicker because of framework-wide simplifications.
    • We now match Swift's naming convention for some big combinators (esp. map and flatMap). Update your code accordingly!
    • Currying syntax has been removed framework-wide and labels have been made more semantically meaningful.
    • Gen.fromShufflingElementsOf is unbelievably fast now.
    • pseudo-Equatable instances for modifier types have been removed.
    • More things have been documented and what documentation there is has been improved.
    • Quantifiers and property binders have more informative @warn_unused_result messages.

    Finally, for the pièce de résistance, cover has been made a first-class citizen of the framework. For those times when a particular property of a spec should hold a certain percentage of the time, you can ask SwiftCheck how well it's been covered. If we don't rise to meet your set goals, the test fails!

    screen shot 2016-01-08 at 2 53 31 am Source code(tar.gz)
    Source code(zip)
  • v0.4.4(Jan 4, 2016)

  • v0.4.3(Dec 28, 2015)

    • Framework build times continue to improve.
    • Fixes an issue where building the framework in release mode would cause strange crashes (h/t @kykim).
    Source code(tar.gz)
    Source code(zip)
  • v0.4.2(Dec 16, 2015)

    • Fixes an issue where 32-bit devices could cause the random number generator to crash with an overflow exception.
    • Compile times for the entire framework have been significantly improved (h/t @robrix)
    Source code(tar.gz)
    Source code(zip)
  • v0.4.1(Nov 21, 2015)

    This release fixes a major issue caused by <*> not re-jiggering its generators. This could cause tests in the previous release (v0.4.0) that use the same call in the traditional applicative form to generate the same values in many cases (h/t @QF5690 for the report).

    As a consequence, generators for all types are now significantly more robust and have instances rewritten and optimized for the new deterministic RNG.

    Source code(tar.gz)
    Source code(zip)
    SwiftCheck.framework.zip(12.30 MB)
  • v0.4.0(Nov 19, 2015)

    SwiftCheck now has a new in-library pseudo-RNG that enables tests to be replayed argument-for-argument with just two seed values.

    For example, given this property:

    property("Obviously wrong") <- forAll { (x : Int, y : Int, c : Int) in (x > y) ==> x + c < y + c }
    

    SwiftCheck takes care of printing all the nitty-gritty details.

    screen shot 2015-11-18 at 7 20 33 pm

    So all you have to do is feed those values back into the testing loop:

    let (seedl, seedr) = (618383975, 8314)
    let replayArgs = CheckerArguments(replay: .Some(StdGen(seedl, seedr), 1))
    property("Obviously wrong", arguments: replayArgs) <- forAll { (x : Int, y : Int, c : Int) in (x > y) ==> x + c < y + c }.verbose
    

    In addition, this release comes with a number of bug fixes and improvements:

    • SwiftCheck now uses the @exported attribute with XCTest so you can just import SwiftCheck without needing to import XCTest.
    • Test cases that throw exceptions now also print counterexamples. Whoops!
    • The CheckerArguments structure is now easier to create and use (h/t @kballard).
    • The StdGen structure is now public.
    Source code(tar.gz)
    Source code(zip)
  • v0.3.2(Oct 15, 2015)

    With this release, all functions now catch exceptions thrown inside their respective testing blocks.

    Naturally, a thrown exception is treated as an automatic failure of the test case. If you wish to treat it as a passing case, see Property.invert and Property.expectFailure.

    We've also added support for the existential quantifier exists in addition to the universal forAll.

    Source code(tar.gz)
    Source code(zip)
  • v0.3.1(Sep 14, 2015)

  • v0.3.0(Sep 12, 2015)

    Good News Everyone!

    SwiftCheck now fully supports Swift 2.0. Other changes include:

    • The syntax for testing has changed slightly.
    property["Numa Numa"] = forAll { //... }
    

    now becomes

    property("Numa Numa") <- forAll { //... }
    
    • We have a shiny new tutorial playground where you can explore SwiftCheck to your heart's content.
    • Support for tvOS has been added!
    • Handling of signs in signed integer generation has been fixed (h/t @brentleyjones).
    • SwiftCheck is now fully integrated with XCTest. Failures will display on the properties and lines that caused them.
    • You get an Arbitrary instance, and you get an Arbitrary instance. Everybody gets an Arbitrary instance!
    • Those classes and structures that could not be made Arbitrary now have Arbitrary-like extensions for use with forAllShrink.
    • The WitnessedArbitrary protocol has been added for higher-order types like [Arbitrary] and Set<Arbitrary>.
    • The IsoOf modifier has been added. It works like ArrowOf, but in 2 directions instead of just 1.
    • Operators have been split into their own package and standardized across TypeLift.
    • shrink is now an optional requirement for an Arbitrary instance. If none is provided, SwiftCheck will assume you don't want to shrink at all.
    • conjamb, the non-deterministic conjunction combinator, has been added.
    • The Large modifier has been added. It allows integers to be generated in their entire range rather than be bounded by their Generator's size.
    • Arguments that modify the testing loop can now be passed in a property call.
                                                      // Re-checks all tests with this generator and size.
    let args = CheckerArguments(  replay: Optional.Some((newStdGen(), 10))
                                                     // Doubles the required number of tests to pass 
                            , maxAllowableSuccessfulTests: 200
                                                     // We can no longer discard.
                            , maxAllowableDiscardedTests: 0
                                                     // 10x the default size of the test case.
                            , maxTestCaseSize: 1000
                            )
    
    property("Discards forbidden", arguments: args) <- forAll { (x : UInt) in
        return Discard()
    }.expectFailure
    
    Source code(tar.gz)
    Source code(zip)
  • v0.2.6(Jun 1, 2015)

  • v0.2.5(Jun 1, 2015)

    • ArrowOf now memoizes its arguments and behaves like a proper function.
    • Many more types have CoArbitrary instances so they can be used with ArrowOf.
    Source code(tar.gz)
    Source code(zip)
  • v0.2.4(May 31, 2015)

    • Adds a new combinator, Gen.weighted. It's like frequency but doesn't require so many Gen.pure calls.
    • The constraints on Gen.frequency and Gen.weighted are now relaxed to allow any SequenceType
    • The shrinker loop is thinner, quicker, and much much less memory hungry than before for complex shrinks.
    • Fixes a bug in the shrinker where tests would not report the number of shrinks they'd gone through.
    • Propagates expected failures and outputs the correct Result type for them.
    • Compile times have been significantly improved for some files (h/t @robrix).
    Source code(tar.gz)
    Source code(zip)
  • v0.2.3(May 29, 2015)

    • Fixed iOS deployment target [and therefore the Carthage build]
    • Introduced 4 new operators to make complex testing possible (==>, ====, <?>, ^&&^, ^||^; Implication, Verbose Equality, Conjunction, Disjunction)
    • Generator Combinators are now static methods instead of top-level functions.
    • Numerous internal fixes to the testing loop
    • Removal of extraneous methods and data structures.
    • Array shrinks now consume less memory and complete much faster
    • Documentation has been added to every user-facing method
    • forAllShrink(_:_:_:) has a relaxed generic parameter for easier integration with custom testing
    • Verbose checks are now possible. Using verbose checks, SwiftCheck will dump all test cases to console with the result of each test.
    • When labelling properties, SwiftCheck will output a distribution graph of the percentage of passing test cases a property has gone through.
    • Conjunct and Disjunct properties used with labelling will report distribution graphs for each individual property
    Source code(tar.gz)
    Source code(zip)
Owner
TypeLift
Libraries to simplify development of Swift programs by utilising the type system.
TypeLift
This is a Swift port of Ruby's Faker library that generates fake data.

This is a Swift port of Ruby's Faker library that generates fake data. Are you still bothered with meaningless randomly character strings? Just relax

Vadym Markov 1.6k Jan 3, 2023
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
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
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
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
AppiumLibrary is an appium testing library for RobotFramework

Appium library for RobotFramework Introduction AppiumLibrary is an appium testing library for Robot Framework. Library can be downloaded from PyPI. It

Serhat Bolsu 327 Dec 25, 2022
Network testing for Swift

DVR DVR is a simple Swift framework for making fake NSURLSession requests for iOS, watchOS, and OS X based on VCR. Easy dependency injection is the ma

Venmo 650 Nov 3, 2022
The Swift (and Objective-C) testing framework.

Quick is a behavior-driven development framework for Swift and Objective-C. Inspired by RSpec, Specta, and Ginkgo. // Swift import Quick import Nimbl

Quick 9.6k Dec 31, 2022
Snapshot testing tool for iOS and tvOS

SnapshotTest is a simple view testing tool written completely in Swift to aid with development for Apple platforms. It's like unit testing for views.

Pär Strindevall 44 Sep 29, 2022
UI Testing Cheat Sheet and Examples.

UI Testing Cheat Sheet This repository is complementary code for my post, UI Testing Cheat Sheet and Examples. The post goes into more detail with exa

Joe Masilotti 2.1k Dec 25, 2022
Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS

Mockingbird Mockingbird was designed to simplify software testing, by easily mocking any system using HTTP/HTTPS, allowing a team to test and develop

FARFETCH 183 Dec 24, 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
Fastbot is a model-based testing tool for modeling GUI transitions to discover app stability problems

Fastbot is a model-based testing tool for modeling GUI transitions to discover app stability problems. It combines machine learning and reinforcement learning techniques to assist discovery in a more intelligent way.

Bytedance Inc. 446 Dec 29, 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
For Testing APIs of NYTimes

NYTimes-APIs For Testing APIs of NYTimes Mark Dennis Diwa ?? To run the app: Open terminal first then run pod install. Open workspace. Run the app on

Mark Dennis Diwa 0 Nov 23, 2021
Multivariate & A/B Testing for iOS and Mac

This library is no longer being maintained. You can continue to use SkyLab in your projects, but we recommend switching another solution whenever you

Mattt 792 Dec 15, 2022
Remote configuration and A/B Testing framework for iOS

MSActiveConfig v1.0.1 Remote configuration and A/B Testing framework for iOS. Documentation available online. MSActiveConfig at a glance One of the bi

Elevate 78 Jan 13, 2021
AB testing framework for iOS

ABKit Split Testing for Swift. ABKit is a library for implementing a simple Split Test that: Doesn't require an HTTP client written in Pure Swift Inst

Recruit Marketing Partners Co.,Ltd 113 Nov 11, 2022
Keep It Functional - An iOS Functional Testing Framework

IMPORTANT! Even though KIF is used to test your UI, you need to add it to your Unit Test target, not your UI Test target. The magic of KIF is that it

KIF Framework 6.2k Dec 29, 2022