$30 off During Our Annual Pro Sale. View Details »

BDD DEVit

BDD DEVit

Pawel Dudek

June 11, 2018
Tweet

More Decks by Pawel Dudek

Other Decks in Programming

Transcript

  1. Behavior Driven
    Development
    Paweł Dudek

    View Slide

  2. View Slide

  3. Behavior Driven
    Development

    View Slide

  4. What is a unit test?
    4

    View Slide

  5. A method by which individual units of
    source code, sets of one or more program
    modules together with associated control
    data, usage procedures, and operating
    procedures are tested to determine if they
    are fit for use.
    --Kolawa, Adam; Huizinga, Dorota (2007)
    5

    View Slide

  6. Um, what?
    6

    View Slide

  7. A method by which individual units of
    source code, sets of one or more program
    modules together with associated control
    data, usage procedures, and operating
    procedures are tested to determine if they
    are fit for use.
    --Kolawa, Adam; Huizinga, Dorota (2007)
    7

    View Slide

  8. What is an app?
    8

    View Slide

  9. An app is a set of
    behaviors created by a
    programmer and
    expected by the user.
    9

    View Slide

  10. We, humans, have a
    limited cognition.
    10

    View Slide

  11. We can’t always ‘load’
    all of the code of our
    app into our memory.
    11

    View Slide

  12. This means that we
    can, by accident,
    change the behavior of
    the app.
    12

    View Slide

  13. Preserving behavior of
    software systems is
    hard.
    13

    View Slide

  14. Enter unit tests.
    14

    View Slide

  15. Unit test is a failsafe
    to make sure app
    behavior is preserved.
    15

    View Slide

  16. Test Driven
    Development
    16

    View Slide

  17. Tests drive the way you
    code
    17

    View Slide

  18. Always write test first
    18

    View Slide

  19. Tests influence
    architecture of your
    app
    19

    View Slide

  20. Tests tell you whether
    your design became
    too complex
    20

    View Slide

  21. There is no such thing
    as untestable behavior
    Only untestable code
    21

    View Slide

  22. Have to simulate
    behavior of your
    dependency
    dependency
    dependency?
    22

    View Slide

  23. Need to fake seven
    objects?
    23

    View Slide

  24. Need to call five
    functions to simulate
    a behavior?
    24

    View Slide

  25. Hard to specify clear
    requirements?
    25

    View Slide

  26. Overcomplicated
    design
    26

    View Slide

  27. Thinked-through
    design
    27

    View Slide

  28. First consumer of your
    API
    28

    View Slide

  29. There is no such thing
    as untestable behavior
    Only untestable code
    29

    View Slide

  30. What is testable code?
    30

    View Slide

  31. Testable code
    ==
    Good Architecture
    31

    View Slide

  32. What is good
    architecture?
    32

    View Slide

  33. SOLID
    Object-Oriented
    Design
    Sandi Metz
    https://www.youtube.com/watch?v=v-2yFMzxqwU
    33

    View Slide

  34. Behavior Driven Development
    Test Driven Development
    34

    View Slide

  35. What's the difference?
    35

    View Slide

  36. BDD aims to improve
    certain aspects of TDD
    36

    View Slide

  37. BDD tries to help you
    know what to test
    37

    View Slide

  38. When writing tests
    don’t think ‘tests’
    38

    View Slide

  39. Think about
    ‘behaviors’
    39

    View Slide

  40. Think about examples
    how your object
    should behave
    40

    View Slide

  41. Objects behavior
    ==
    Public interface
    41

    View Slide

  42. You should be only testing public
    interface of your objects
    Not internal implementation
    42

    View Slide

  43. Work outside-in
    43

    View Slide

  44. Ubiquitous language
    44

    View Slide

  45. Quick & Nimble
    Swift BDD Frameworks
    45

    View Slide

  46. class DolphinSpec: QuickSpec {
    override func spec() {
    it("is friendly") {
    expect(Dolphin().isFriendly).to(beTruthy())
    }
    it("is smart") {
    expect(Dolphin().isSmart).to(beTruthy())
    }
    }
    }

    View Slide

  47. class DolphinSpec: QuickSpec {
    override func spec() {
    it("is friendly") {
    expect(Dolphin().isFriendly).to(beTruthy())
    }
    it("is smart") {
    expect(Dolphin().isSmart).to(beTruthy())
    }
    }
    }

    View Slide

  48. describe("a dolphin") {
    describe("its click") {
    it("is loud") {
    let click = Dolphin().click()
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    let click = Dolphin().click()
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  49. describe("a dolphin") {
    describe("its click") {
    it("is loud") {
    let click = Dolphin().click()
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    let click = Dolphin().click()
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  50. describe("a dolphin") {
    describe("its click") {
    it("is loud") {
    let click = Dolphin().click()
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    let click = Dolphin().click()
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  51. describe("a dolphin") {
    var dolphin: Dolphin!
    beforeEach { dolphin = Dolphin() }
    describe("its click") {
    var click: Click!
    beforeEach { click = dolphin.click() }
    it("is loud") {
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  52. describe("a dolphin") {
    var dolphin: Dolphin!
    beforeEach { dolphin = Dolphin() }
    describe("its click") {
    var click: Click!
    beforeEach { click = dolphin.click() }
    it("is loud") {
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  53. describe("a dolphin") {
    var dolphin: Dolphin!
    beforeEach { dolphin = Dolphin() }
    describe("its click") {
    var click: Click!
    beforeEach { click = dolphin.click() }
    it("is loud") {
    expect(click.isLoud).to(beTruthy())
    }
    it("has a high frequency") {
    expect(click.hasHighFrequency).to(beTruthy())
    }
    }
    }

    View Slide

  54. Real world example
    54

    View Slide

  55. class PhotoUploadViewController: UIViewController {
    let photoUploader: PhotoUploader
    required init(photoUploader: PhotoUploader) {
    self.photoUploader = photoUploader
    super.init(nibName: nil, bundle: nil)
    navigationItem.rightBarButtonItem = UIBarButtonItem(
    title: "Done",
    target: self,
    action: #selector(PhotoUploadViewController.onTap(_:)))
    }
    @objc func onTap(_ item: UIBarButtonItem?) {
    photoUploader.upload(photo: UIImage()) { success in
    print("\(success)")
    }
    }
    }

    View Slide

  56. protocol PhotoUploader {
    func upload(photo: UIImage, completion: (_ success: Bool) -> Void)
    }
    class MockPhotoUploader: PhotoUploader {
    fileprivate(set) var photoUploadCalled: Bool = false
    func upload(photo: UIImage, completion: (_ success: Bool) -> Void) {
    self.photoUploadCalled = true
    }
    }

    View Slide

  57. var sut: PhotoUploadViewController!
    var mockPhotoUploader: MockPhotoUploader!
    beforeEach {
    mockPhotoUploader = MockPhotoUploader()
    sut = PhotoUploadViewController(photoUploader: mockPhotoUploader)
    }
    afterEach {
    sut = nil
    }

    View Slide

  58. // non-bdd
    describe("right bar button item") {
    var rightBarButtonItem: UIBarButtonItem?
    beforeEach {
    rightBarButtonItem = sut.navigationItem.rightBarButtonItem
    }
    it("should have a target") {
    let actual = rightBarButtonItem?.target as? PhotoUploadViewController
    expect(actual).to(equal(sut))
    }
    it("should have an action") {
    let expected = #selector(PhotoUploadViewController.onTap(_:))
    expect(rightBarButtonItem?.action).to(equal(expected))
    }
    }

    View Slide

  59. // non-bdd
    describe("right bar button item action") {
    beforeEach {
    let rightBarButtonItem = sut.navigationItem.rightBarButtonItem
    sut.onTap(rightBarButtonItem)
    }
    it("should tell the photo uploader to upload photo") {
    expect(fakePhotoUploader.photoUploadCalled).to(beTruthy())
    }
    }

    View Slide

  60. // bdd
    describe("right bar button item") {
    var rightBarButtonItem: UIBarButtonItem?
    beforeEach {
    rightBarButtonItem = sut.navigationItem.rightBarButtonItem
    }
    describe("when it is tapped") {
    beforeEach {
    rightBarButtonItem?.specSimulateTap()
    }
    it("should tell the photo uploader to upload photo") {
    expect(fakePhotoUploader.photoUploadCalled).to(beTruthy())
    }
    }
    }

    View Slide

  61. BDD - recap
    » Tests influence your architecture
    » No such thing as untestable behavior
    » Think examples/behaviors, not tests
    » Don’t test implementation, work outside-in
    » Use ubiquitous language to make examples easily
    understandable
    61

    View Slide

  62. Further read:
    » https://speakerdeck.com/paweldudek/bdd-devit
    » https://github.com/paweldudek/good-tdd-stuff
    » https://github.com/paweldudek/bdd-presentation
    Getting in touch:
    » @eldudi
    » [email protected]
    62

    View Slide