Write the Docs Amsterdam: Test your API docs!

Write the Docs Amsterdam: Test your API docs!

7b2e4bf7ecca28e530e1c421f0676c0b?s=128

Honza Javorek

February 19, 2018
Tweet

Transcript

  1. Test your API docs! It's tested or it's broken Honza

    Javorek WTD Amsterdam Feb 2018
  2. Honza honzajavorek.cz @honzajavorek

  3. None
  4. None
  5. pycon.cz @pyconcz

  6. None
  7. Interface of libraries #!/usr/bin/env python import django

  8. Interface of systems curl https://api.github.com/

  9. Technical?

  10. User interface!

  11. 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?
  12. Requests: HTTP for Humans

  13. >>> 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?
  14. How do you design the interface?

  15. Eating your own dog food

  16. 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'
  17. Writing tests first helps to design the interface TDD

  18. 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'
  19. Writing down behavior first helps to design the interface BDD

  20. 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
  21. Testable documentation!

  22. 1. Design 2. Test 3. Implement

  23. 1. Think, agree, promise 2. Test the promise 3. Fulfill

    the promise
  24. How did Kenneth design requests?

  25. None
  26. Before I start writing a single line of code, I

    write the README and fill it with usage examples. I pretend that the module I want to build is already written and available, and I write some code with it. “ https://www.kennethreitz.org/essays/how-i-develop-things-and-why
  27. RDD

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

  29. # 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
  30. 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
  31. Interface in README = Essential interface user expects

  32. README must not get out of sync with code

  33. How do we ensure implementation matches the design?

  34. python -m doctest README.md doctest

  35. language: "python" python: - "3.6" script: - "python -m doctest

    README.md" Continuous Integration
  36. 1. Think, agree, README 2. Test the README 3. Fulfill

    the README
  37. What if we could design and test web APIs like

    this?
  38. # 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
  39. None
  40. None
  41. dredd API.md http://localhost:8000 Dredd

  42. None
  43. None
  44. None
  45. None
  46. None
  47. None
  48. None
  49. Go JavaScript Perl PHP Python Ruby Rust

  50. language: "python" python: - "3.6" before_install: - "npm install -g

    dredd" script: - "dredd API.md http://localhost:8000" Continuous Integration
  51. None
  52. None
  53. What about the OpenAPI Spec? (fka Swagger)

  54. YAML

  55. 1. Think, agree, API desc. 2. Test the API desc.

    3. Fulfill the API desc.
  56. Testing implementation against design allows you designing before implementing

  57. Designing before implementing allows you better design

  58. Dredd allows you better design

  59. 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
  60. None
  61. github.com/apiaryio/dredd @honzajavorek