Filosofando sobre Testes

Me
Eu faço perguntas em reuniões que eu não sei nada e reunião explode; não é de propósito
Me chamaram de Mini Hitler
... mas hoje eu vim botar os filho dos outro no go horse.

WARNING

... e eu sou meio não-ortodoxo sobre testes.

Agenda:

  • TDD Kent Beck Style.
  • Fast Test, Slow Tests.
  • Explosão de testes.
  • Coverage.
TDD: Where it went wrong

Ian Cooper: "TDD, where did it all go wrong"

TDD

Kent Beck:

  • "Run in isolation", nothing more, nothing less.
  • "Avoid testing implementation details, test behaviours"

TDD

Discussões como "qual a unidade a ser testada" é que geraram coisas como BDD e ATDD (Acceptance Test-Driven Development).

TDD

Reddit: Devo escrever testes para a validação interna do Django?

SIM!

... bom, talvez sim.

TDD

Nossos testes End-to-End.

Gary Bernhardt: Fast Test, Slow Test

Fast Test, Slow Test

  • Testar modelos.
  • Testar views.
  • Testar controllers.

... soa familiar?

Fast Test, Slow Test

  • Desenvolvedores podem testar, rapidamente, suas alterações.
  • Não significa que testes de integração não são necessários, mas estes podem ser rodados no CI.

... continua soando familiar?

Fast Test, Slow Test

Qual o valor desse teste para o meu projeto?

Fast Test, Slow Test

Se eu estou escrevendo testes do meu modelo sem que seja integrado com o meu controller, eu estou testando o que?

Aderência a arquitetura do projeto.

Fast Test, Slow Test

Se o teste que verifica a funcionalidade para o usuário (integração) produz mais valor que o teste que garante a aderência do projeto à uma arquitetura, qual teste deve ser rodado primariamente?

A Explosão dos Testes Lentos

Explosão de Testes Lentos

"Testes de integração são lentos, então eu tenho que rodar-los menos ou ter menos desses testes."

Explosão de Testes Lentos

  • Rode apenas o teste da alteração que está sendo feita.
  • Rode os testes relacionados.
  • Deixe todos os demais testes para o CI.

Coverage

Coverage

Se os testes indicam quais funcionalidades devem estar presentes...

... coverage vai indicar quais partes de código não tem nada a ver com as funcionalidades que vão ser entregues.

Coverage

Exemplo


class Client:
    def __init__(self, name):
        self.name = name
                        

Coverage

Exemplo

Novo requisito: "somente aceitar nomes válidos, que tem 2 palavras ou mais."

Coverage

Exemplo


def _multiple_names(name):
    split_names = name.split(' ')
    return len(split_names) > 1

def _validate_name(name):
    if not _multiple_names(name):
        raise Exception("Invalid name")
    return name

class Client:
    def __init__(self, name):
        self.name = _validate_name(name)
                        

Coverage

Exemplo


import pytest

def test_single_name():
    assert not _multiple_names('Cher')

def test_multiple_name():
    assert _multiple_names('Julio Biason')
                        

Coverage

Exemplo


def test_valid_name():
    _validate_name('Julio Biason')

def test_invalid_name():
    with pytest.raises(Exception):
        _validate_name('Cher')
                        

Coverage

Exemplo


def test_client_error():
    with pytest.raises(Exception):
        Client(name='Cher')

def test_client():
    Client(name='Julio Biason')
                        

Coverage

Exemplo


$ pytest client.py
==== test session starts ====
rootdir: /home/jbiason/unitt, inifile:
collected 6 items

client.py ......

==== 6 passed in 0.03 seconds ====
                        

Coverage

Exemplo


$ pytest --cov=client client.py
==== test session starts ====
plugins: cov-2.4.0
collected 6 items

client.py ......

---- coverage: platform linux, python 3.4.3-final-0 ----
Name        Stmts   Miss  Cover
-------------------------------
client.py      25      0   100%

==== 6 passed in 0.11 seconds ====
                        

Coverage

Exemplo

"Não podemos perder a Cher, a Xuxa, a Madonna, a Björk e o String como clientes!"

Coverage

Exemplo


class Client:
    def __init__(self, name):
        self.name = name
                        

Coverage

Exemplo


==== FAILURES ====
____ test_client_error ____

    def test_client_error():
        with pytest.raises(Exception):
>           Client(name='Cher')
E           Failed: DID NOT RAISE <class 'Exception'>

client.py:37: Failed
==== 1 failed, 5 passed in 0.63 seconds ====
                        

Coverage

Exemplo


$ pytest  client.py
==== test session starts ====
rootdir: /home/jbiason/unitt, inifile:
plugins: cov-2.4.0
collected 6 items

client.py ......

==== 6 passed in 0.03 seconds ====
                        

Coverage

Exemplo


$ pytest --cov=client  client.py
==== test session starts ====
rootdir: /home/jbiason/unitt, inifile:
plugins: cov-2.4.0
collected 6 items

client.py ......

---- coverage: platform linux, python 3.4.3-final-0 ----
Name        Stmts   Miss  Cover
-------------------------------
client.py      24      0   100%

==== 6 passed in 0.12 seconds ====
                        

Coverage

Exemplo

Encontre o erro.

Coverage

100% de cobertura de testes é possível.

... desde que esteja disposto a escrever código que testa funcionalidades do usuário e não conformidade de arquitetura e código não coberto seja apagado.

Perguntas?