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

Because Web API Testing Should Be Easy

Because Web API Testing Should Be Easy

Web APIs are often called REST APIs, which recently became a part of the everyday life of many Python developers. Sure, it can be a blast to build an API with frameworks like Django or Flask, but writing tests for it can be a tedious drag: asserting every single HTTP code, set of headers, JSON responses, error states… you know the drill. Despite all that you do, you still need to assure your API clients won’t be exposed to any unexpected surprises. At Apiary, we've developed an Open Source testing framework called Dredd, which has baked in first-class Python support. It does all the heavy lifting and boring stuff for you while allowing you to alter the test cases with arbitrary Python code. Let Judge Dredd do your API justice.

Honza Javorek

March 13, 2017
Tweet

More Decks by Honza Javorek

Other Decks in Technology

Transcript

  1. Because Web API
    Testing Should Be Easy
    Honza Javorek

    View full-size slide

  2. Because Web API
    Testing Should Be Easy
    Honza Javorek

    View full-size slide

  3. Design APIs for humans
    and test what you promised
    Honza Javorek

    View full-size slide

  4. Honza
    honzajavorek.cz

    View full-size slide

  5. Interface of libraries
    pip install django

    View full-size slide

  6. Interface of systems
    curl https://api.github.com/repos/django/django
    curl http://www.cnb.cz/cs/financni_trhy/devizovy_trh/
    kurzy_devizoveho_trhu/denni_kurz.txt

    View full-size slide

  7. User interface

    View full-size slide

  8. import urllib2
    gh_url = 'https://api.github.com'
    req = urllib2.Request(gh_url)
    password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm()
    password_manager.add_password(None, gh_url, 'user', 'pass')
    auth_manager = urllib2.HTTPBasicAuthHandler(password_manager)
    opener = urllib2.build_opener(auth_manager)
    urllib2.install_opener(opener)
    handler = urllib2.urlopen(req)
    print handler.getcode()
    print handler.headers.getheader('content-type')
    Do you like this interface?

    View full-size slide

  9. Requests: HTTP for Humans

    View full-size slide

  10. >>> r = requests.get('https://api.github.com/user',
    ... auth=('user', 'pass'))
    >>> r.status_code
    200
    >>> r.headers['content-type']
    'application/json; charset=utf8'
    >>> r.encoding
    'utf-8'
    >>> r.text
    '{"type":"User"...'
    >>> r.json()
    {'disk_usage': 368627, 'private_gists': 484, ...}
    Do you like this interface?

    View full-size slide

  11. How did Kenneth do it?

    View full-size slide

  12. How do you design the
    interface?

    View full-size slide

  13. def test_basic_building():
    req = requests.Request()
    req.url = 'http://kennethreitz.org/'
    req.data = {'life': '42'}
    pr = req.prepare()
    assert pr.url == req.url
    assert pr.body == 'life=42'

    View full-size slide

  14. Writing tests first
    helps to design the interface
    TDD

    View full-size slide

  15. test > RED > implement > test > GREEN
    req = requests.Request()
    req.url = 'http://kennethreitz.org/'
    req.data = {'life': '42'}
    pr = req.prepare()
    assert pr.url == req.url
    assert pr.body == 'life=42'

    View full-size slide

  16. Writing down behavior first
    helps to design the interface
    BDD

    View full-size slide

  17. Feature: Status code
    Background:
    Given you expect HTTP status code "200"
    Scenario: Different real response status
    When real status code is "500"
    Then Gavel will set some error for "status code"
    And Request or Response is NOT valid
    Scenario: Response status code match
    When real status code is "200"
    Then Gavel will NOT set any errors for "status code"
    And Request or Response is valid
    Gherkin / Cucumber

    View full-size slide

  18. Testable documentation!

    View full-size slide

  19. RDD
    Readme Driven Development
    http://tom.preston-werner.com/2010/08/23/readme-driven-
    development.html

    View full-size slide

  20. # Requests
    The `requests` library allows you to perform HTTP
    requests from your Python code.
    ## Example
    ```python
    >>> r = requests.get('https://github.com')
    >>> r.status_code
    200
    ```
    ## License
    MIT
    README.md

    View full-size slide

  21. Readme Driven Development
    • chance to think through the project first
    • docs are ready - no need to write them retroactively
    • your team can use the interface before it exists
    • easy to discuss the interface with everyone

    View full-size slide

  22. Interface in README
    =
    Essential interface user expects

    View full-size slide

  23. README must not
    get out of sync with code

    View full-size slide

  24. How do we ensure
    implementation
    matches the design?

    View full-size slide

  25. python -m doctest README.md
    doctest

    View full-size slide

  26. language: "python"
    python:
    - "3.6"
    script:
    - "python -m doctest README.md"
    Continuous Integration

    View full-size slide

  27. What if we could design and
    test web APIs like this?

    View full-size slide

  28. # Calendar API
    The API gives you various means to work with date and time.
    ## GET /now
    Provides you with current date and time.
    - Response 200 (application/json)
    ```json
    {
    "day": 29,
    "month": 2,
    "year": 2017,
    "hour": 11,
    "minute": 45,
    "second": 38
    }
    ```
    API.md

    View full-size slide

  29. dredd API.md http://localhost:8000
    Dredd

    View full-size slide

  30. language: "python"
    python:
    - "3.6"
    before_install:
    - "npm install -g dredd"
    script:
    - "dredd API.md http://localhost:8000"
    Continuous Integration

    View full-size slide

  31. Testing implementation against design
    allows you
    designing before implementing

    View full-size slide

  32. Designing before implementing
    allows you
    better design

    View full-size slide

  33. Dredd
    allows you
    better design

    View full-size slide

  34. Remember
    • think first, design first, docs first, test first
    • discuss the the interface design before implementing
    • use the interface before implementing (mocks, tests)
    • have your interface design as a single source of truth
    • test implementation against the design

    View full-size slide