Slide 1

Slide 1 text

DVR Networking tes-ng in Swi/.

Slide 2

Slide 2 text

Hi, I'm @soffes.

Slide 3

Slide 3 text

DVR is this thing I made when I worked at Venmo.

Slide 4

Slide 4 text

Inspired by VCR for Ruby.

Slide 5

Slide 5 text

Records networking and plays it back in tests.

Slide 6

Slide 6 text

Using DVR

Slide 7

Slide 7 text

// Example XCTest func testTimeline() { let expectation = expectationWithDescription("Networking") // Your network client let client = APIClient() client.getTimeline { success in XCTAssertTrue(success) expectation.fulfill() } waitForExpectationsWithTimeout(1, handler: nil) }

Slide 8

Slide 8 text

// DVR Session let dvr = Session(cassetteName: "timeline") // Pass into initializer let client = APIClient(session: dir)

Slide 9

Slide 9 text

class APIClient { let session: NSURLSession init(session: NSURLSession = NSURLSession.sharedSession()) { self.session = session } }

Slide 10

Slide 10 text

func getTimeline(completion: Bool -> Void) { let request = NSURLRequest(URL: timelineURL) session.dataTaskWithRequest(request) { _, _, _ completion(true) }.resume() }

Slide 11

Slide 11 text

DVR Internals

Slide 12

Slide 12 text

class Session: NSURLSession { let cassetteName: String let backingSession: NSURLSession init(cassetteName: StringbackingSession: NSURLSession = NSURLSession.sharedSession()) { self.cassetteName = cassetteName self.backingSession = backingSession super.init() } override func dataTaskWithRequest(request: NSURLRequest) -> NSURLSessionDataTask { return SessionDataTask(session: self, request: request) } }

Slide 13

Slide 13 text

Call resume() & then magic.

Slide 14

Slide 14 text

class SessionDataTask: NSURLSessionDataTask { override func resume() { // Playback cassette on disk or record } }

Slide 15

Slide 15 text

Casse%es are stored as JSON.

Slide 16

Slide 16 text

{ "name" : "example", "interactions" : [ { "recorded_at" : 1434688721.440751, "request" : { "method" : "GET", "url" : "http:\/\/example.com" } "response" : { "body" : "hello", "status" : 200, "url" : "http:\/\/example.com\/", "headers" : { "Cache-Control" : "max-age=604800", "Content-Type" : "text\/plain", "Last-Modified" : "Fri, 09 Aug 2013 23:54:35 GMT", "Content-Length" : "5" } } } ] }

Slide 17

Slide 17 text

Why this over mocks?

Slide 18

Slide 18 text

Mocks are inherently fragile. You have to couple your tes7ng code with the implementa7on details of your test. — Dave Abrahams, Protocol-Oriented Programming in Swi3

Slide 19

Slide 19 text

class DisabledSession: NSURLSession { override func dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -> Void) -> NSURLSessionDataTask? { XCTFail("Networking disabled") return nil } }

Slide 20

Slide 20 text

github.com/venmo/DVR

Slide 21

Slide 21 text

Thanks