Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Property-Based Testing with SwiftCheck

TJ Usiyan
September 06, 2016

Property-Based Testing with SwiftCheck

These are the slides for my talk at try! swift NYC on 2016-09-02

TJ Usiyan

September 06, 2016
Tweet

More Decks by TJ Usiyan

Other Decks in Programming

Transcript

  1. How are test cases difficult to write? • We need

    to find all of the meaninful test cases 5
  2. How are test cases difficult to write? • We need

    to find all of the meaninful test cases • We need to organize them in a meaningful way 5
  3. How are test cases difficult to write? • We need

    to find all of the meaninful test cases • We need to organize them in a meaningful way • How many cases are enough? 5
  4. How can we find test cases? • Randomness • Reproducible

    • Simplify failing case (when possible) 6
  5. 7

  6. Arbitrary public protocol Arbitrary { /// The generator for this

    particular type. public static var arbitrary: SwiftCheck.Gen<Self> { get } } 9
  7. Conforming to Arbitrary public static var arbitrary: SwiftCheck.Gen<Rational> { return

    Gen.compose { comp in return Rational(comp.generate(), comp.generate()) } } 11
  8. Filtering Generated values extension Rational : Arbitrary { public static

    var arbitrary: SwiftCheck.Gen<Rational> { return Gen<Rational>.compose { comp in let denGen = Int.arbitrary.suchThat { $0 != 0 } return Rational( comp.generate(), comp.generate(using: denGen) } } 14
  9. Round Trip Properties property("round trip to string") <- forAll {

    (i: Int) in return Int(i.description)! == i } 17
  10. Commuting Diagram Properties property("base64 encoding commutes with Apple's ") <-

    forAll { (data: Data) in return data.base64Encoding == data.myBase64Encoding } 20
  11. Properties func testAddition() { property("Addition is commutative") <- forAll {

    (i: Rational, j: Rational) in return (i + j) == (j + i) } } 21
  12. Properties property("Addition is commutative 2") <- forAll { (i: Rational,

    j: Rational, k: Rational) in let caseOne = (i + j + k) let caseTwo = ((i + j) + k) let caseThree = (i + (j + k)) return (caseOne == caseTwo) && (caseTwo == caseThree) } 22
  13. Replay let (seedl, seedr) = (1640744780, 1403884642) let replayArgs =

    CheckerArguments(replay: .some(StdGen(seedl, seedr), 3)) property("x / y", arguments: replayArgs) <- forAll { … 24
  14. Mixed Testing func testEquality() { let oneOverTwo = Rational(1, 2)!

    let twoOverFour = Rational(2, 4)! let ichiUeNi = Rational(1, 2)! XCTAssertEqual(oneOverTwo == twoOverFour, true) XCTAssertEqual(twoOverFour == ichiUeNi, true) XCTAssertEqual(ichiUeNi == oneOverTwo, true) XCTAssertEqual(oneOverTwo ≡ twoOverFour, false) XCTAssertEqual(twoOverFour ≡ ichiUeNi, false) XCTAssertEqual(ichiUeNi ≡ oneOverTwo, true) let halfIntMax = (Int.max / 2) - 1 property("value holds") <- forAll { (value: Rational) in return ((value.numerator < halfIntMax) && (value.denominator < halfIntMax)) ==> { let doubledSame = Rational(value.numerator * 2, value.denominator * 2)! return (value == doubledSame) && ((value ≡ doubledSame) == false) } } } 25
  15. So? • Less time writing tests? • Better tests •

    Easier diagnosis • You will and should still write test cases! 27