minicurso testando restful web services

Post on 12-Apr-2017

511 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Charles Kilesse@chkile

Testando RESTful Web Services

• REST e RESTful web services

• URI

• Métodos

• Estrutura de mensagem HTTP

• Códigos de resposta

• Testes exploratórios com Postman

• Testes automatizados com Python

• Boas práticas em automação

• Autenticação

Agenda

O que é REST?

REST“Representational state transfer”

Estilo arquitetural da World Wide Web

Trata de papéis e interações

Comunicação tipicamente por HTTP

RESTCliente/Servidor

Sem estado

Interface uniforme

Tá, mas… ondeentram as APIs?

RESTful Web ServicesUniform Resource Identifier (URI)

Métodos (ou verbos) HTTP

Media type (JSON, XML…)

Sem estado

URIhttp://chaordic.com.br/

http://chaordic.com.br:80/

http://user:password@chaordic.com.br/

http://chaordic.com.br/caminho/recurso

URIhttp://chaordic.com.br/?chave=valor

http://chaordic.com.br/?c=v&c2=v2

http://chaordic.com.br/#fragmento

Verbos (métodos) HTTP

GET POST PUT DELETE

OPTIONS

HEADCONNECT TRACE

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

Verbos (métodos) HTTP

GET POST PUT DELETE

OPTIONS

HEADCONNECT TRACE

http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

este curso

- Solicita uma representação do recurso especificado.

- Apenas recupera dados!

verbo

GET

- Envia uma entidade a ser processada.

- Dados da entidade e informações adicionais devem ser enviados no corpo da requisição.

verbo

POST

- Envia uma entidade a ser armazenada sob uma determinada URI.

- Substitui a entidade, se já existir. Se não existir, cria uma na URI especificada.

verbo

PUT

- O recurso especificado se fode é deletado.

verbo

DELETE

Verbos (métodos) HTTP

GET POST PUT DELETE

seguroidempotente

idempotente

idempotente

VERBO URI

CABEÇALHOS

CORPO

Mãos à obra!

http://www.google.com/chrome/https://www.getpostman.com/

GET http://dummyimage.com/600x400

GET http://dummyimage.com/600x400/ff0000/fff

GET http://dummyimage.com/600x400/ff0000/fff&text=October

Test

GET http://dummyimage.com/640x400/%%%

WTF!?

HTTP Status CodesInformação: 1xx

Sucesso: 2xxRedirecionamento: 3xx

Erro do Cliente: 4xxErro do Servidor: 5xx

http://www.cheatography.com/kstep/cheat-sheets/http-status-codes/

GET http://jsonplaceholder.typicode.com/posts/1

POST http://jsonplaceholder.typicode.com/posts

Content-Type: application/json

{ "title": "Titulo 1", "body": "Body 1", "userID": 1}

PUT http://jsonplaceholder.typicode.com/posts/1

Content-Type: application/json

{ "id": 1, "title": "Titulo 1", "body": "Body 1", "userID": 1}

DELETE http://jsonplaceholder.typicode.com/posts/1

#partiuautomação

https://www.jetbrains.com/pycharm/http://www.python-requests.org/

https://www.python.org/

import unittestimport requests

class TestCases(unittest.TestCase): pass

if __name__ == "__main__": unittest.main()

class TestCases(unittest.TestCase): def test_get_200(self): msg="Test not implemented." self.fail(msg)

def test_get_200(self): uri = "http://apps.testinsane.com/rte/status/200" r = requests.get(uri)

msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)

def test_get_401(self): uri = "http://apps.testinsane.com/rte/status/401" r = requests.get(uri)

msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 401, msg)

POST

Enviar um payload

Header “content-type”

Validar conteúdo da resposta

import json

uri = "http://apps.testinsane.com/rte/status/200"

headers = { "content-type": "application/json"}

payload = { "id": "1", "name": "OctoberTest"}

r = requests.post(uri, data=json.dumps(payload), headers=headers)

r_payload = r.json()

msg = "Incorrect status code: %d" % r.status_codeself.assertEqual(r.status_code, 200, msg)

msg = "Incorrect id: %s" % r_payload.get("id")self.assertEqual(r_payload.get("id"), payload.get("id"), msg)

msg = "Incorrect name: %s" % r_payload.get("name")self.assertEqual(r_payload.get("name"), "payload.get("name")", msg)

def test_post_200(self): uri = "http://apps.testinsane.com/rte/status/200" headers = { "content-type": "application/json" } payload = { "id": "1", "name": "OctoberTest" } r = requests.post(uri, data=json.dumps(payload), headers=headers) r_payload = r.json()

msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)

msg = "Incorrect id: %s" % r_payload.get("id") self.assertEqual(r_payload.get("id"), payload.get("id"), msg)

msg = "Incorrect name: %s" % r_payload.get("name") self.assertEqual(r_payload.get("name"), "payload.get("name")", msg)

E se o host mudar?E se os endpoints mudarem?

E se a resposta tiver234565639582 chaves?

MODULARIZAR!

api_helper.py

import requestsimport json

HOST = "http://apps.testinsane.com/rte%s"

def get(endpoint): uri = HOST % endpoint return requests.get(uri)

def post(endpoint, headers, payload): uri = HOST % endpoint return requests.post(uri, data=json.dumps(payload), headers=headers)

json_helper.py

import logging

def compare(expected, actual): all_match = True for key, value in expected.iteritems(): if not value==actual.get(key): all_match = False return all_match

* E se o valor de uma chave for uma lista ou objeto?

import api_helperimport json_helper

class TestCases(unittest.TestCase): def test_get_200(self): r = api_helper.get("/status/200") msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)

msg = "Response payload did not match the request payload."

self.assertTrue(json_helper.compare(payload, r_payload), msg)

BOAS PRÁTICAS!

Organizando os testes com o padrão “3 As”

def test_get_200(self): # Arrange endpoint = "/status/200" # Act r = api_helper.get(endpoint) # Assert msg = "Incorrect status code: %d" % r.status_code self.assertEqual(r.status_code, 200, msg)

Funcional x Estruturallogin(username, password)

ou

login_as(user)

enter_name(username)

enter_password(password)

click_login()

Use “lint checkers”!(PEP8: Style Guide for Python Code)

Código de teste entrega valor e deve ser tratado como “código de produção”

YAGNI, DRY and KISS whenever you can!

AUTENTICAÇÃO

Basic AuthenticationAutenticação simples (cabeçalhos

estáticos)

Encoding em Base64 durante a trasnferência

Não criptografado

Digest Access Authentication

Aplica hash MD5 nas credenciais

Mais seguro que Basic Auth

OAuthBaseado na criação de “tokens” de

acesso para clientes terceiros

Aplicativos acessando sua conta do Facebook ou CI SAAS acessando um

repositório no GitHub, por exemplo

from requests.auth import HTTPBasicAuth

basic_auth = HTTPBasicAuth('user', 'pass')

requests.get('https://api.github.com/user', auth=basic_auth)

from requests.auth import HTTPDigestAuth

digest_auth = HTTPDigestAuth('user', 'pass')

from requests_oauthlib import OAuth1

oauth = OAuth1('YOUR_APP_KEY', 'YOUR_APP_SECRET',

'USER_OAUTH_TOKEN',

'USER_OAUTH_TOKEN_SECRET')

http://docs.python-requests.org/en/latest/user/authentication/

Links e referênciashttps://github.com/chkile/minicurso-octobertest

http://www.w3.org/Protocols/

http://docs.python-requests.org/en/latest/

https://www.python.org/dev/peps/pep-0008/

charles@chaordic.com.br

charles.kilesse@outlook.com

@chkile

top related