Slide 1

Slide 1 text

Test your API docs! It's tested or it's broken Honza Javorek FOSDEM 2018

Slide 2

Slide 2 text

Honza honzajavorek.cz @honzajavorek

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

PRAGUE

Slide 7

Slide 7 text

Czech Republic

Slide 8

Slide 8 text

PYCON.CZ

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

Interface of libraries #!/usr/bin/env python import django

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Technical?

Slide 13

Slide 13 text

User interface!

Slide 14

Slide 14 text

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?

Slide 15

Slide 15 text

Requests: HTTP for Humans

Slide 16

Slide 16 text

>>> 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?

Slide 17

Slide 17 text

How do you design the interface?

Slide 18

Slide 18 text

Eating your own dog food

Slide 19

Slide 19 text

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'

Slide 20

Slide 20 text

Writing tests first helps to design the interface TDD

Slide 21

Slide 21 text

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'

Slide 22

Slide 22 text

Writing down behavior first helps to design the interface BDD

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Testable documentation!

Slide 25

Slide 25 text

1. Design 2. Test 3. Implement

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

How did Kenneth design requests?

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

RDD

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

# 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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Interface in README = Essential interface user expects

Slide 35

Slide 35 text

README must not get out of sync with code

Slide 36

Slide 36 text

How do we ensure implementation matches the design?

Slide 37

Slide 37 text

python -m doctest README.md doctest

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

1. Think, agree, README 2. Test the README 3. Fulfill the README

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

# 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

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

No content

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

No content

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

No content

Slide 52

Slide 52 text

Go JavaScript Perl PHP Python Ruby Rust

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

No content

Slide 55

Slide 55 text

No content

Slide 56

Slide 56 text

What about the OpenAPI Spec? (fka Swagger)

Slide 57

Slide 57 text

YAML

Slide 58

Slide 58 text

1. Think, agree, API desc. 2. Test the API desc. 3. Fulfill the API desc.

Slide 59

Slide 59 text

Testing implementation against design allows you designing before implementing

Slide 60

Slide 60 text

Designing before implementing allows you better design

Slide 61

Slide 61 text

Dredd allows you better design

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text

github.com/apiaryio/dredd @honzajavorek