FISL 16

TDD com Mock e Orientação a Objetos com Python


Bernardo Fontes


Porto Alegre/RS

10 de Julho de 2015

Olar!

twitter.com/bbfontes

github.com/berinhard

garimpo.fm

pessoas.cc

bernardoxhc@gmail.com

berinhard.github.io/talks/


slideshare.net/bernardofontes


Avaliem, por favor

Quem são vocês?

# Roteiro - ## Básicos de Orientação a Objetos - ## Test-driven Development (TDD) - ## TDD Com Mocks - ## Dúvidas
## Básicos de Orientação a Objetos - ### *Não é fazer getter e setter*
# Orientação a Objetos? - ## Estratégia de **Design** - ## Definição de **Estado** - ## **Encapsulamento** - ## Colaboração com troca de **Mensagens**
Fluxo de Objetos
## E se cerveja fosse um objeto? - ### **Cervejada** seria um **atributo** - ### **Gelada** seria um **estado** - ### **Ser servida** seria um **comportamento**
## Exemplo Python ```python class Cerveja(object): quantidade = 600 def __init__(self, temperatura, cervejada): self.temperatura = temperatura self.cervejada = cervejada def esta_gelada(self): return self.temperatura < 5 #> def servir(self, quantidade): if self.quantidade < quantidade: #> quantidade = self.quantidade self.quantidade -= quantidade ############################# cerveja = Cerveja(temperatura=2, cervejada=True) cerveja.esta_gelada() cerveja.servir(150) ```
## Exemplo Python ```python from datetime import date class Ticket(object): def __init__(self, buyer, price, schedule_date): self.buyer = buyer self.price = price self.schedule_date = schedule_date def has_expired(self): return date.today() > self.schedule_date ############################# ticket = Ticket("Bernardo", 10, date(2015, 10, 10)) if not ticket.has_expired(): #processa ticket válido ```

Grafo de Dependência

Fluxo de Objetos
## Pensando o Design com OO - ### **S** - ### **O** - ### **L** - ### **I** - ### **D**
## Pensando o Design com OO - ### **S**ingle Responsibility Principle - ### **O**pen Closed Principle - ### **L**iskov Substitution Principle - ### **I**nterface Segregation Principle - ### **D**ependency Inversion Principle
## Single Responsibility Principle - ### Nunca deve exstir mais de **uma razão para modificar** algo em uma classe.
## Open Closed Principle - ### Toda classe deve estar **aberta para extensão**, mas **fechada para modificação**
## Referências

Applying UML and Patterns

Object Mentor

Curso Objetos Pythônicos

## Test-driven Development (TDD)
## Primeiro o teste ```python class Ticket(TestCase): def test_3_returns_fizz(self): self.assertEqual("fizz", fizzbuzz(3)) ```
## Primeira implementação ```python def fizzbuzz(number): return "fizz" class Ticket(TestCase): def test_3_returns_fizz(self): self.assertEqual("fizz", fizzbuzz(3)) ```
## Refactoring ### Refatoração é o processo de **modificar um sistema** de software para **melhorar a estrutura** interna do código **sem alterar seu comportamento** externo.

Coding Dojo!!!

DojoRio

DojoPuzzles

Processo de Design

Processo de Design

Processo que viabiliza Design

## Design Emergente - Refatoração - Remoção de Acoplamentos - Duplicidade
## **Exploração** e **Descoberta** - ## cansativo...
## Sem Evidência do Design ```python class TestTicketManager(TestCase): def test_expire_ticket(self): ticket = Ticket.objects.create(id=30) self.assertFalse(ticket.expired) manager = TicketManager() manager.expire_ticket(ticket_id=30) ticket = Ticket.objects.get(id=30) self.assertTrue(ticket.expired) ```
## Difícil de começar a testar no alto nível - ### **Infra** vs **Domínio**
## Referências

Curso TDD - J. B. Rainsberger

## TDD com Mock
## Mock - ### **Simulam** funcionamento de objetos - ### Respeitam **API** dos objetos - ### Viabilizam maneira de fazer **asserções**
## Foco do Domíno nas **Mensagens** - ### Menos **Estado** e mais **Colaboração**
## Need-Driven Development - ### Código **criado** só se **necessário**
## Implementação de Testes Top-Down
## TODO List - ### Recuperar Ingresso - ### Expirá-lo - ### Notificar Usuário
## Entry Point ```python class TestTicketManager(TestCase): def test_expire_ticket(self): manager = TicketManager() manager.expire_ticket(ticket_id=30) ```
## Recuperar Ingresso ```python from mock import Mock class TestTicketManager(TestCase): def test_expire_ticket(self): tickets_repository = Mock(TicketsRepository) tickets_repository.get_by_id.return_value = Ticket() manager = TicketManager(tickets_repository) manager.expire_ticket(ticket_id=30) tickets_repository.get_by_id.assert_called_once_with(30) ```
```python class TicketManager(object): def __init__(self, tickets_repository): self.repository = tickets_repository def expire_ticket(self, ticket_id): self.repository.get_by_id(ticket_id) class Ticket(object): pass class TicketsRepository(object): def get_by_id(self, id): "Must return a Ticket object" pass ```
## Expirar Ingresso ```python from mock import Mock class TestTicketManager(TestCase): def test_expire_ticket(self): ticket = Mock(Ticket) tickets_repository = Mock(TicketsRepository) tickets_repository.get_by_id.return_value = ticket manager = TicketManager(tickets_repository) manager.expire_ticket(ticket_id=30) tickets_repository.get_by_id.assert_called_once_with(30) ticket.expire.assert_called_once_with() ```
```python class TicketManager(object): def __init__(self, tickets_repository): self.repository = tickets_repository def expire_ticket(self, ticket_id): ticket = self.repository.get_by_id(ticket_id) ticket.expire() class Ticket(object): def expire(self): pass class TicketsRepository(object): def get_by_id(self, id): "Must return a Ticket object" pass ```
## Notificar Usuário ```python from mock import Mock class TestTicketManager(TestCase): def test_expire_ticket(self): ticket = Mock(Ticket) tickets_repository = Mock(TicketsRepository) tickets_repository.get_by_id.return_value = ticket notificator = Mock(TicketNotificator) manager = TicketManager(tickets_repository, notificator) manager.expire_ticket(ticket_id=30) tickets_repository.get_by_id.assert_called_once_with(30) ticket.expire.assert_called_once_with() notificator.notify_expired.assert_called_once_with(ticket) ```
```python class TicketManager(object): def __init__(self, tickets_repository, notificator): self.repository = tickets_repository self.notificator = notificator def expire_ticket(self, ticket_id): ticket = self.repository.get_by_id(ticket_id) ticket.expire() self.notificator.notify_expired(ticket) class Ticket(object): def expire(self): pass class TicketsRepository(object): def get_by_id(self, id): "Must return a Ticket object" pass class TicketNotificator(object): def notify_expired(self, ticket): pass ```
## Novos testes para garantir **contrato** e **funcionamento unitário** dos componentes
## Respeito ao contrato é **tudo** - ### Comportamentos de **Entrada** - ### Comportamentos de **Saída**
## Referências

Mock Roles, not Objects

GOOS Guided By Tests

pip install mock

11ª PythonBrasil

Obrigado!

Bernardo Fontes

twitter.com/bbfontes

github.com/berinhard

garimpo.fm

pessoas.cc

bernardoxhc@gmail.com