<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">

        <title>Filosofando sobre Tests</title>

        <meta name="description" content="A framework for easily creating beautiful presentations using HTML">
        <meta name="author" content="Hakim El Hattab">

        <meta name="apple-mobile-web-app-capable" content="yes">
        <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">

        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, minimal-ui">

        <link rel="stylesheet" href="reveal.js/css/reveal.css">
        <link rel="stylesheet" href="reveal.js/css/theme/night.css" id="theme">

        <!-- Code syntax highlighting -->
        <link rel="stylesheet" href="reveal.js/lib/css/zenburn.css">

        <!-- Printing and PDF exports -->
        <script>
            var link = document.createElement( 'link' );
            link.rel = 'stylesheet';
            link.type = 'text/css';
            link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
            document.getElementsByTagName( 'head' )[0].appendChild( link );
        </script>

        <!--[if lt IE 9]>
        <script src="lib/js/html5shiv.js"></script>
        <![endif]-->

        <style type="text/css" media="screen">
            .happy {
                color: yellow;
            }

            .reveal section img {
                border: none;
            }

            .reveal ul.empty {
                list-style: none outside;
            }

			li {
				display: block;
			}

            .semi-opaque {
                background-color: rgba(0, 0, 0, 0.7);
            }
        </style>
    </head>

    <body>
        <div class="reveal">
            <div class="slides">
                <section>
                    <section data-background="_images/tdd-kentbeck-book.jpg" data-header>
                        <h1 class="semi-opaque">Filosofando sobre Testes</h1>
                    </section>
                </section>

                <section>
                    <section>
                        <img src="_images/avatar-20170726.png" alt="Me" style="float:left;width:200px;" class="no-border">

                        <div>
                            <ul class="empty">
                                <li>Júlio Biason</li>
                                <li>https://functional.cafe/@juliobiason</li>
                                <li>julio.biason@pm.me</li>
                                <li><a href="http://presentations.juliobiason.net">http://presentations.juliobiason.net</a></li>
                            </ul>
                        </div>
                    </section>

                    <section>
                        <img src="_images/start-a-fight.jpg" alt="Eu faço perguntas em reuniões que eu não sei nada e reunião explode; não é de propósito" class='stretch'/>

						<aside class="notes">
							Eu sou famoso (ou era) por entrar eu reuniões, fazer uma
							pergunta e a reunião explodir em discussões (úteis, diga-se
							de passagem). Mas eu não fazia isso de propósito.
						</aside>
                    </section>

                    <section>
                        <img src="_images/filho-do-capeta.jpg" alt="... mas hoje eu vim botar os filho dos outro no go horse." class='stretch'/>

						<aside class="notes">
							... mas hoje eu vim "botá os fio dos otro no Go Horse, fazê
							eles escrevê menos teste"
						</aside>
                    </section>

					<section>
						<h2><span style="color:red">WARNING</span></h2>

						<img src="_images/patriarch.jpg" alt="... e eu sou meio não-ortodoxo sobre testes."/>
					</section>

					<aside class="notes">
						Um aviso: muitas das coisas que eu vou discutir aqui,
						não são as coisas que normalmente são discutidas quando
						se fala sobre testes.

						Fiquem avisados que muitas pessoas não concordam comigo.
					</aside>
                </section>

				<section>
					<h2>Agenda:</h2>

					<ul>
						<li>TDD Kent Beck Style.</li>
						<li>Fast Test, Slow Tests.</li>
						<li>Explosão de testes lentos.</li>
						<li>Coverage.</li>
					</ul>
				</section>

                <section>
                    <section>
                        <img src="_images/tdd-where-it-went-wrong.png" alt="TDD: Where it went wrong" class="stretch"/>
                        <p>Ian Cooper: <a href="https://vimeo.com/68375232">"TDD, where did it all go wrong"</a></p>

						<aside class="notes">
							Boa parte das coisas que eu vou discutir aqui foram
							influenciadas por esse video do Ian Cooper, em que
							ele discute coisas sobre TDD no estilo discutido
							por Kent Beck, que foi quem escreveu o livro que
							trouxe o termo "TDD" para as massas.
						</aside>
                    </section>

                    <section>
                        <h2>TDD</h2>

                        <p>Kent Beck:</p>

                        <ul>
                            <li>"Run in isolation", nothing more, nothing less.</li>
                            <li>"Avoid testing implementation details, test behaviours"</li>
                        </ul>

						<aside class="notes">
							No seu livro, Kent Beck diz que "unit tests"
							significam "rodam de forma isolada, nada mais, nada
							menos". Esse é o _único_ significado de "unit
							test"; "testes unitários" e não "testes de
							unidade", na tradução correta.

							Outra coisa que Kent Beck fala no seu livro é que
							devem ser testados comportamentos, não
							implementação. Pensem um pouco sobre isso: Se
							estamos testando comportamento ao invés de
							implementações, como é que podemos escrever um
							teste de um objeto ou uma função? O que sabemos é
							que uma aplicação tem um protocolo de entrada de
							dados (terminal, web, GUI) e um protocolo de saída
							(provavelmente o mesmo que o de entrada, no sentido
							contrário) e como essa aplicação tem que se
							comportar com relação à entrada.
						</aside>
                    </section>

                    <section>
                        <h2>TDD</h2>

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

						<aside class="notes">
							Se vocês pensarem, essa questão de como uma
							aplicação deve se comportar é exatamente que fez
							com o BDD (behaviour driven development) e ATDD
							surgiram -- e eles não são diferentes do que o que
							Kent Beck quis dizer com o TDD.
						</aside>
                    </section>

                    <section>
                        <h2>TDD</h2>

                        <p>Reddit: <a href="https://www.reddit.com/r/django/comments/5bearg/should_i_write_unit_tests_for_djangos_built_in/" target="_blank">Devo escrever testes para a validação interna do Django?</a></p>

						<aside class="notes">
							Uma boa pergunta uma vez surgiu no Reddit do Django
							foi "Devo eu criar um teste para um campo inteiro
							quando o Django já verifica que um campo inteiro
							não possa ter caracteres?"
						</aside>

                        <h1 class="fragment">SIM!</h1>

						<aside class="notes">
							A questão do teste não é relacionado com o tipo de
							campo, mas o _significado_ do campo.

							Exemplo, se o campo for "ano de nascimento",
							obviamente "AAAA" não é um ano válido, e o Django
							vai barrar isso se eu definir o campo como inteiro.
							Mas eu poderia usar um campo caracter (por um
							motivo qualquer) e eu teria que testar por ser um
							inteiro.

							Mas porque eu usaria um caracter ao invés de um
							inteiro?

							"Teste comportamento, não implementação".
						</aside>
					</section>

                    <section>
                        <h2>TDD</h2>

                        <p>Nossos testes End-to-End.</p>

                        <aside class="notes">
							Explicação do que aconteceu com os testes do
							gerenciador de alertas.
                        </aside>
                    </section>
				</section>

				<section>
					<section>
						<img src="_images/tdd-fast-test-slow-test.png" alt="" class="stretch"/>

						<p>Gary Bernhardt: <a href="https://www.youtube.com/watch?v=RAxiiRPHS9k" target="_blank">Fast Test, Slow Test</a></p>
					</section>

					<section>
						<img src="_images/tdd-fast-test-slow-test-2.png" alt=""/ class="stretch">
					</section>

					<section>
						<h2>Fast Test, Slow Test</h2>

						<ul>
							<li>Testar modelos.</li>
							<li>Testar views.</li>
							<li>Testar controllers.</li>
						</ul>

						<p class="fragment">... soa familiar?</p>
					</section>

					<section>
						<h2>Fast Test, Slow Test</h2>

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

						<p class="fragment">... continua soando familiar?</p>
					</section>

					<section>
						<h2>Fast Test, Slow Test</h2>

						<p>Qual o valor desse teste para o meu projeto?</p>
					</section>

					<section>
						<h2>Fast Test, Slow Test</h2>

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

						<p class="fragment"><u>Aderência a arquitetura do projeto</u>.</p>
					</section>

					<section>
						<h2>Fast Test, Slow Test</h2>

						<p>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?</p>
					</section>
				</section>

				<section>
					<section>
						<h2>A Explosão dos Testes Lentos</h2>
					</section>

					<section>
						<h2>Explosão de Testes Lentos</h2>

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

					<section>
						<h2>Explosão de Testes Lentos</h2>

						<ul>
							<li>Rode apenas o teste da alteração que está sendo feita.</li>
							<li class="fragment">Rode os testes relacionados.</li>
							<li class="fragment">Deixe todos os demais testes para o CI.</li>
						</ul>

					</section>
				</section>

                <section>
					<section>
						<h2>Coverage</h2>
					</section>

					<section>
						<h2>Coverage</h2>

						<p>Se os testes indicam quais funcionalidades devem estar
						presentes...</p>

						<p class="fragment">... coverage vai indicar quais partes
						de código não tem nada a ver com as funcionalidades que
						vão ser entregues.</p>
					</section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
class Client:
    def __init__(self, name):
        self.name = name
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <p>Novo requisito: "somente aceitar nomes válidos, que tem 2
                            palavras ou mais."</p>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
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)
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
import pytest

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

def test_multiple_name():
    assert _multiple_names('Julio Biason')
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
def test_valid_name():
    _validate_name('Julio Biason')

def test_invalid_name():
    with pytest.raises(Exception):
        _validate_name('Cher')
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
def test_client_error():
    with pytest.raises(Exception):
        Client(name='Cher')

def test_client():
    Client(name='Julio Biason')
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code data-trim>
$ pytest client.py
==== test session starts ====
rootdir: /home/jbiason/unitt, inifile:
collected 6 items

client.py ......

==== 6 passed in 0.03 seconds ====
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code data-trim>
$ 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 ====
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <p>"Não podemos perder a Cher, a Xuxa, a Madonna, a Björk e o String como clientes!"</p>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code class="python" data-trim>
class Client:
    def __init__(self, name):
        self.name = name
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code data-trim>
==== FAILURES ====
____ test_client_error ____

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

client.py:37: Failed
==== 1 failed, 5 passed in 0.63 seconds ====
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code data-trim>
$ 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 ====
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <pre><code data-trim>
$ 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 ====
                        </code></pre>
                    </section>

                    <section>
                        <h2>Coverage</h2>
						<h3>Exemplo</h3>

                        <p>Encontre o erro.</p>
                    </section>

					<section>
						<h2>Coverage</h2>

						<p>100% de cobertura de testes é possível.</p>

						<p class="fragment">... 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.</p>
					</section>
                </section>

                <section data-background='_images/thats-all-folks.jpg'>
                    <section>
                        <h1 class="fragment semi-opaque">Perguntas?</h1>

						<div class="fragment semi-opaque">
							<ul>
                                <li>Júlio Biason</li>
                                <li>https://functional.cafe/@juliobiason</li>
                                <li>julio.biason@pm.me</li>
                                <li><a href="http://presentations.juliobiason.net">http://presentations.juliobiason.net</a></li>
							</ul>
						</div>
                    </section>
                </section>
            </div>
        </div>

        <script src="reveal.js/lib/js/head.min.js"></script>
        <script src="reveal.js/js/reveal.js"></script>

        <script>
            // Full list of configuration options available at:
            // https://github.com/hakimel/reveal.js#configuration
            Reveal.initialize({
                controls: true,
                progress: true,
                history: true,
                center: true,
                // showNotes: true,

                transition: 'slide', // none/fade/slide/convex/concave/zoom

                // Optional reveal.js plugins
                dependencies: [
                    { src: 'reveal.js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
                    { src: 'reveal.js/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
                    { src: 'reveal.js/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
                    { src: 'reveal.js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
                    { src: 'reveal.js/plugin/zoom-js/zoom.js', async: true },
                    { src: 'reveal.js/plugin/notes/notes.js', async: true }
                ]
            });
        </script>

    </body>
</html>