TDD com Mock e Orientação a Objetos
Bernardo Fontes
Serra - ES
29 de Abril de 2017
# 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?
- ### **Quantidade** seria um **atributo**
- ### **Gelada** seria um **estado**
- ### **Ser servida** seria um **comportamento**
## Exemplo Python
```python
class Cerveja(object):
def __init__(self, temperatura):
self.temperatura = temperatura
self.quantidade = 600
def esta_gelada(self):
return self.temperatura < 5 #>
def servir(self, quantidade):
if self.quantidade < quantidade: #>
quantidade = self.quantidade
self.quantidade -= quantidade
#############################
In : cerveja = Cerveja(temperatura=-2)
In : cerveja.esta_gelada()
Out: True
In : cerveja.servir(150)
In : cerveja.quantidade
Out: 450
```
## Exemplo do Ingresso
```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
```
## 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 existir 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**
Object Mentor
## Test-driven Development (TDD)
## Primeiro o teste
```python
class FizzbuzzTests(TestCase):
def test_3_returns_fizz(self):
self.assertEqual("fizz", fizzbuzz(3))
```
## Primeira implementação
```python
def fizzbuzz(number):
return "fizz"
class FizzbuzzTests(TestCase):
def test_3_returns_fizz(self):
self.assertEqual("fizz", fizzbuzz(3))
```
Coding Dojo!!!
## 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**
## Implementação de Testes Top-Down
## Need-Driven Development
- ### Código **criado** só se **necessário**
## 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):
raise NotImplementedError
```
## 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):
raise NotImplementedError
class TicketsRepository(object):
def get_by_id(self, id):
raise NotImplementedError
```
## 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):
raise NotImplementedError
class TicketsRepository(object):
def get_by_id(self, id):
raise NotImplementedError
class TicketNotificator(object):
def notify_expired(self, ticket):
raise NotImplementedError
```
## Respeito ao contrato é **tudo**
- ### Comportamentos de **Entrada**
- ### Comportamentos de **Saída**
## Novos testes para garantir **contrato** e **funcionamento unitário** dos componentes
Mock Roles, not Objects
GOOS Guided By Tests
Python Sudeste 2017
Python Sudeste 2018 no ES ???