Slide 1

Slide 1 text

Testing without Xcode Kyle Fuller

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Open Source Libraries

Slide 9

Slide 9 text

Existing code on other Platforms

Slide 10

Slide 10 text

Editors

Slide 11

Slide 11 text

Who am I

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

Agenda • How other communities do testing • How testing is done in Swift today • How we can do testing in Swift in the future

Slide 14

Slide 14 text

How do other communities solve testing? • Ruby ! • C / C++ ䷢

Slide 15

Slide 15 text

Testing in Ruby describe '.authenticate' do context 'when logged in' do it { is_expected.to respond_with 200 } end end

Slide 16

Slide 16 text

Testing in Ruby $ rspec test.rb Finished in 0.0016 seconds (files took 0.2153 seconds to load) 5 examples, 0 failures

Slide 17

Slide 17 text

Testing in C / C++ int main(int argc, const char *argv[]) { user_t user = user_alloc("kyle"); // Test User's name assert(user.name == "kyle"); // Test User's priviledges assert(user.is_admin, 0); return 0; }

Slide 18

Slide 18 text

Testing in C / C++ $ clang test.c -o tests $ ./tests Tests passed

Slide 19

Slide 19 text

Testing in C / C++ #define CATCH_CONFIG_MAIN #include "catch.hpp" TEST_CASE( "Factorials are computed", "[factorial]" ) { REQUIRE( Factorial(1) == 1 ); REQUIRE( Factorial(2) == 2 ); REQUIRE( Factorial(3) == 6 ); REQUIRE( Factorial(10) == 3628800 ); }

Slide 20

Slide 20 text

Test Framework ~= Test Runner

Slide 21

Slide 21 text

Testing in Swift • assert • XCTest • Quick (uses XCTest) • Spectre • Ploughman • Custom testing • CLI tool

Slide 22

Slide 22 text

Testing with assert

Slide 23

Slide 23 text

assert()

Slide 24

Slide 24 text

assert(person.name == "Kyle")

Slide 25

Slide 25 text

assert(person.name == "Kyle", "Persons name should be Kyle")

Slide 26

Slide 26 text

struct Person: CustomStringConvertible { let name: String var description: String { return name } } let kyle = Person(name: "Kyle") assert(kyle.description == "Kyle", "conforms to CustomStringConvertible")

Slide 27

Slide 27 text

$ swift test.swift

Slide 28

Slide 28 text

Testing in a playground

Slide 29

Slide 29 text

Demo

Slide 30

Slide 30 text

assert() testing Upsides • Extremely simple • Built into stdlib Downsides • Hard to manage • Crashes your process, not all tests are ran

Slide 31

Slide 31 text

XCTest

Slide 32

Slide 32 text

CompileSwift normal x86_64 /Users/kyle/Projects/QueryKit/QueryKit/Tests/SortDescriptorTests.swift cd /Users/kyle/Projects/QueryKit/QueryKit /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -frontend -c /Users/kyle/Projects/QueryKit/QueryKit/Tests/AttributeTests.swift /Users/kyle/Projects/QueryKit/QueryKit/Tests/QueryKitTests.swift -primary-file /Users/kyle/Projects/QueryKit/QueryKit/Tests/SortDescriptorTests.swift ... Test Suite 'All tests' started at 2016-07-08 01:59:05.062 Test Suite 'QueryKitTests.xctest' started at 2016-07-08 01:59:05.063 Test Suite 'AttributeTests' started at 2016-07-08 01:59:05.063 Test Case '-[QueryKitTests.AttributeTests testAscendingSortDescriptor]' started. Test Case '-[QueryKitTests.AttributeTests testAscendingSortDescriptor]' passed (0.002 seconds). Test Case '-[QueryKitTests.AttributeTests testAttributeExpression]' started. Test Case '-[QueryKitTests.AttributeTests testAttributeExpression]' passed (0.001 seconds). Test Suite 'SortDescriptorTests' passed at 2016-07-08 01:59:05.362. Executed 2 tests, with 0 failures (0 unexpected) in 0.001 (0.003) seconds Test Suite 'QueryKitTests.xctest' passed at 2016-07-08 01:59:05.363. Executed 75 tests, with 0 failures (0 unexpected) in 0.240 (0.300) seconds Test Suite 'All tests' passed at 2016-07-08 01:59:05.364. Executed 75 tests, with 0 failures (0 unexpected) in 0.240 (0.302) seconds ** TEST SUCCEEDED **

Slide 33

Slide 33 text

xctool & xcpretty

Slide 34

Slide 34 text

https://github.com/supermarin/xcpretty

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

Test Anywhere Protocol (TAP) ... ok 71 - testTypeSafeOrderBySortDescriptor ok 72 - testTypeSafeOrderBySortDescriptors ok 73 - testTypeSafeRelatedFilterPredicate ok 74 - testAscendingSortDescriptor ok 75 - testDescendingSortDescriptor 1..75

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

How does XCTest work? $ xcrun -sdk macosx xctest Usage: xctest

Slide 39

Slide 39 text

import XCTest class PersonTests : XCTestCase { func testCustomStringConvertible() { let kyle = Person(name: "Kyle") XCTAssertEqual(kyle.description, "Kyle") } }

Slide 40

Slide 40 text

How does XCTest work without Objective-C? import XCTest extension PersonTests { static var allTests : [(String, PersonTests -> () throws -> Void)] { return [ ("testCustomStringConvertible", testCustomStringConvertible), ] } } XCTMain([ testCase(PersonTests.allTests), ])

Slide 41

Slide 41 text

No content

Slide 42

Slide 42 text

Spectre http://spectre.fuller.li/

Slide 43

Slide 43 text

describe("Person") { let kyle = Person(name: "Kyle") $0.it("has a description") { try expect(kyle.description) == "Kyle" } }

Slide 44

Slide 44 text

describe("Person") { let kyle = Person(name: "Kyle") $0.it("has a description") { try expect(kyle.description) == "Kyle" } }

Slide 45

Slide 45 text

try expect(person.description) == "Kyle"

Slide 46

Slide 46 text

func == (lhs: Expectation, rhs: T?) throws

Slide 47

Slide 47 text

Reporters

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

Custom Reporters

Slide 51

Slide 51 text

Testing in Playground

Slide 52

Slide 52 text

No content

Slide 53

Slide 53 text

Demo

Slide 54

Slide 54 text

Spectre Runner

Slide 55

Slide 55 text

$ ./tests --help FLAGS: --tap - Test Anywhere Protocol Reporter --t - Dot Reporter

Slide 56

Slide 56 text

https://github.com/Quick/Quick/

Slide 57

Slide 57 text

import Quick import Nimble class TableOfContentsSpec: QuickSpec { override func spec() { describe("the 'Documentation' directory") { it("has everything you need to get started") { let sections = Directory("Documentation").sections expect(sections).to(contain("Installing Quick")) } } } }

Slide 58

Slide 58 text

Cucumber

Slide 59

Slide 59 text

Feature: An array Scenario: Appending to an array Given I have an empty array When I add 1 to the array Then I should have 1 item in the array Scenario: Filtering an array Given I have an array with the numbers 1 through 5 When I filter the array for even numbers Then I should have 2 items in the array

Slide 60

Slide 60 text

Ploughman https://github.com/kylef/Ploughman/

Slide 61

Slide 61 text

Given I have an empty array var array: [Int]? = nil given("^I have an empty array$") { _ in array = [] }

Slide 62

Slide 62 text

When I add X to the array when("^I add (\\d) to the array$") { match in let number = Int(match.groups[1])! array.append(number) }

Slide 63

Slide 63 text

Then I should have X items in the array then("^I should have (\\d) items? in the array$") { match in let count = Int(match.groups[1])! try expect(array.count) == count }

Slide 64

Slide 64 text

$ ./runner scenarios/array.gherkin

Slide 65

Slide 65 text

Real World Example

Slide 66

Slide 66 text

Feature: Basic Nest compliance Scenario: Success response status code When I make a GET request to /hello Then I should have a 200 response Scenario: Response header When I make a GET request to /hello Then I should see the header 'Content-Type' with the value 'text/plain' Scenario: Response body When I make a GET request to /hello Then The contents of the body should be 'Hello World' Scenario: Response body (POST) When I make a POST request with body 'Swift' to /hello Then The contents of the body should be 'Hello Swift'

Slide 67

Slide 67 text

$ NestTestSuite --host http://localhost:8080 Features/*.feature -> Basic Nest compliance -> Performing a simple GET request 1 scenarios passed, 0 scenarios failed.

Slide 68

Slide 68 text

Build Tools

Slide 69

Slide 69 text

Xcode

Slide 70

Slide 70 text

DIY $ swiftc Sources/*.swift -module-name Hello -emit-library -emit-module -o Hello.dylib $ swiftc Tests/*.swift -lHello -o run-tests $ ./run-tests

Slide 71

Slide 71 text

DIY - Make (Makefile) Hello.dylib: swiftc Sources/*.swift -module-name Hello -emit-library -emit-module -o Hello.dylib run-tests: Hello.dylib swiftc Tests/*.swift -lHello -o run-tests .PHONY: test test: run-tests ./run-tests

Slide 72

Slide 72 text

$ make test

Slide 73

Slide 73 text

Swift Package Manager

Slide 74

Slide 74 text

import PackageDescription let package = Package( name: "Curassow", dependencies: [ .Package(url: "https://github.com/kylef/Commander.git", majorVersion: 0, minor: 4), ] )

Slide 75

Slide 75 text

$ swift build

Slide 76

Slide 76 text

import PackageDescription let package = Package( name: "Curassow", testDependencies: [ .Package(url: "https://github.com/kylef/Spectre.git", majorVersion: 0, minor: 7), ] )

Slide 77

Slide 77 text

$ swift test

Slide 78

Slide 78 text

LinuxMain.swift import XCTest extension PersonTests { static var allTests : [(String, PersonTests -> () throws -> Void)] { return [ ("testCustomStringConvertible", testCustomStringConvertible), ] } } XCTMain([ testCase(PersonTests.allTests), ])

Slide 79

Slide 79 text

Custom Testing with swift test

Slide 80

Slide 80 text

! swim https://github.com/kylef/swim/

Slide 81

Slide 81 text

swim Goals • swim is compatible with multiple versions of Swift (2.x, 3.x, etc) • swim allows you to test your Swift project with third-party testing frameworks. • swim is compatible with common Swift Package Manager libraries. • swim is easy to install (does not depend on Swift)

Slide 82

Slide 82 text

$ pip install swim

Slide 83

Slide 83 text

Package.swift let package = Package( name: "Stencil", testDependencies: [ .Package(url: "https://github.com/kylef/Spectre", majorVersion: 0), ] ) Terminal $ swim build $ swim test

Slide 84

Slide 84 text

swiftenv https://swiftenv.fuller.li/

Slide 85

Slide 85 text

$ swiftenv install 2.2.1

Slide 86

Slide 86 text

$ swiftenv local DEVELOPMENT-SNAPSHOT-2016-02-08-a

Slide 87

Slide 87 text

$ cat .swift-version DEVELOPMENT-SNAPSHOT-2016-02-08-a

Slide 88

Slide 88 text

$ swift --version Apple Swift version 2.2.1 $ cd Stencil $ swift --version Apple Swift version 3.0-dev

Slide 89

Slide 89 text

swiftenv https://swiftenv.fuller.li/

Slide 90

Slide 90 text

$ brew install kylef/formulae/swiftenv

Slide 91

Slide 91 text

Testing against multiple versions of Swift

Slide 92

Slide 92 text

$ env SWIFT_VERSION=2.2 swift test --clean $ env SWIFT_VERSION=2.3 swift test --clean $ env SWIFT_VERSION=3.0 swift test --clean

Slide 93

Slide 93 text

Swift on Travis CI https://swiftenv.fuller.li/en/latest/ integrations/travis-ci.html

Slide 94

Slide 94 text

os: - linux - osx install: - eval "$(curl -sL https://github.com/kylef/.../swiftenv-install.sh)"

Slide 95

Slide 95 text

script: swift test

Slide 96

Slide 96 text

env: - SWIFT_VERSION=2.2 - SWIFT_VERSION=2.2.1

Slide 97

Slide 97 text

• Early stages for testing OSS Swift • New tools are emerging • Swift Package Manager is evolving • Test against all platforms

Slide 98

Slide 98 text

kylefuller https://fuller.li/talks