FISL 16
TDD com Mock e Orientação a Objetos com Python
Bernardo Fontes
Porto Alegre/RS
10 de Julho de 2015
Avaliem, por favor
# 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**
## 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
## 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**
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.
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**
Curso TDD - J. B. Rainsberger
## 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**
Mock Roles, not Objects
GOOS Guided By Tests
pip install mock
11ª PythonBrasil