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

Writing Testable Code

Bas Broek
November 28, 2019

Writing Testable Code

Workshop at The Swift Alps 2019

Bas Broek

November 28, 2019
Tweet

More Decks by Bas Broek

Other Decks in Programming

Transcript

  1. Writing
    Testable Code
    1 — @basthomas

    View full-size slide

  2. Tests, what
    are they good
    for?
    2 — @basthomas

    View full-size slide

  3. What do you
    want to
    verify?
    3 — @basthomas

    View full-size slide

  4. How do you
    want to verify
    it?
    4 — @basthomas

    View full-size slide

  5. Dependency
    injection:
    three ways
    5 — @basthomas

    View full-size slide

  6. class Network {
    let urlSession: URLSession
    init(urlSession: URLSession = .shared) {
    self.urlSession = urlSession
    }
    }
    6 — @basthomas

    View full-size slide

  7. class Network {
    var urlSession = URLSession.shared
    }
    7 — @basthomas

    View full-size slide

  8. class Network {
    func request(using urlSession: URLSession = .shared) {
    }
    }
    8 — @basthomas

    View full-size slide

  9. Mocking
    through
    Protocols
    9 — @basthomas

    View full-size slide

  10. protocol UIApplicationProtocol {
    func canOpenURL(_ url: URL) -> Bool
    func open(
    _ url: URL,
    options: [UIApplication.OpenExternalURLOptionsKey : Any],
    completionHandler completion: ((Bool) -> Void)?
    )
    }
    extension UIApplication: UIApplicationProtocol {}
    10 — @basthomas

    View full-size slide

  11. Mocking
    through
    Subclassing
    11 — @basthomas

    View full-size slide

  12. class URLSessionDataTaskMock: URLSessionDataTask {
    private let closure: () -> Void
    init(closure: @escaping () -> Void) {
    self.closure = closure
    }
    override func resume() {
    closure()
    }
    }
    12 — @basthomas

    View full-size slide

  13. class URLSessionMock: URLSession {
    var data: Data?
    var error: Error?
    override func dataTask(
    with url: URL,
    completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void
    ) -> URLSessionDataTask {
    let data = self.data
    let error = self.error
    return URLSessionDataTaskMock {
    completionHandler(data, nil, error)
    }
    }
    }
    13 — @basthomas

    View full-size slide

  14. Custom
    asserts
    14 — @basthomas

    View full-size slide

  15. func assertManyThings(
    cookie: Cookie,
    file: StaticString = #file,
    line: UInt = #line
    ) {
    XCTAssertTrue(cookie.isDoughy, file: file, line: line)
    XCTAssertLessThan(cookie.calories, 200, file: file, line: line)
    }
    15 — @basthomas

    View full-size slide

  16. XCTest
    16 — @basthomas

    View full-size slide

  17. XCTUnwrap
    XCTNoThrow
    17 — @basthomas

    View full-size slide

  18. Let's talk
    about "flaky"
    18 — @basthomas

    View full-size slide

  19. 19 — @basthomas

    View full-size slide

  20. Make sure your
    architecture takes
    testability into account
    20 — @basthomas

    View full-size slide

  21. Tests, Code,
    Documentation
    21 — @basthomas

    View full-size slide

  22. Treat your
    test code like
    your real code
    22 — @basthomas

    View full-size slide

  23. Reviews of tests, not
    reviews with tests
    23 — @basthomas

    View full-size slide

  24. "Your" turn: let's take a
    look at something you
    want to have covered
    with tests
    24 — @basthomas

    View full-size slide