Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Nomeando os bois: Algumas técnicas no teste de ...

Nomeando os bois: Algumas técnicas no teste de aplicações

Avatar for Rogério Zambon

Rogério Zambon

March 19, 2015

More Decks by Rogério Zambon

Other Decks in Programming

Transcript

  1. 3 Disclaimer As técnicas apresentadas não são balas de prata

    e não devem ser usadas como se não existisse o amanhã.
  2. 4 Disclaimer É importante conhecer cada técnica para perceber algum

    design smell, pois nem sempre é claro o limite entre cada um.
  3. 6 Com as conhecidas leis de Lehman, os testes se

    tornaram cada vez mais importantes para garantir uma entrega de qualidade. Introdução
  4. 7 Meir Lehman diz que a tendência de um software

    é que "mudará continuamente, com maior complexidade e com qualidade inferior". Introdução
  5. 8 Por conta disso, há anos testes em aplicações são

    usados com dedicação. Os programas espaciais são bons exemplos. Introdução
  6. 9 No entando, mais recentemente, em 2005, Michael Feathers definiu

    alguns itens que diferenciam um teste de unidade de todos os outros. Introdução
  7. • Comunicação com banco de dados; • Tráfego de dados

    pela rede; • Interação com file system; • Impedimento na execução em paralelo; • Dependência de configurações de ambiente; 10 Introdução Não há...
  8. 14

  9. Uma forma de passar dublês, uma dependência agindo como um

    substituto, garantindo a execução do comportamento da classe-alvo. Stubbing 16
  10. Uma forma de passar dublês, uma dependência agindo como um

    substituto, garantindo a execução do comportamento da classe-alvo. Stubbing 17
  11. Uma forma de passar dublês, uma dependência agindo como um

    substituto, garantindo a execução do comportamento da classe-alvo. Stubbing 18
  12. class Authenticator { private $_user; public function __construct($user) { $this->_user

    = $user; } public function login($password) { if ($this->_user->isValid($password)) { return [ "role" => "default", "expire" => 3600 ]; } throw new AuthenticatorException("authentication_error"); } } 20
  13. class Authenticator { private $_user; public function __construct($user) { $this->_user

    = $user; } public function login($password) { if ($this->_user->isValid($password)) { return [ "role" => "default", "expire" => 3600 ]; } throw new AuthenticatorException("authentication_error"); } } Acesso ao banco de dados 21
  14. class User { public function isValid($password) { return "123" ==

    $password; } } class AuthenticatorTest extends PHPUnit_Framework_TestCase { public function testQuandoUmUsuarioEstaAptoASeLogar() { $user = new User; $authenticator = new Authenticator($user); $this->assertEquals( [ "role" => "default", "expire" => 3600 ], $authenticator->login("123") ); } } 22
  15. class User { public function isValid($password) { return "123" ==

    $password; } } class AuthenticatorTest extends PHPUnit_Framework_TestCase { public function testQuandoUmUsuarioEstaAptoASeLogar() { $user = new User; $authenticator = new Authenticator($user); $this->assertEquals( [ "role" => "default", "expire" => 3600 ], $authenticator->login("123") ); } } Dublê (stub) 23
  16. class AuthenticatorTest extends PHPUnit_Framework_TestCase { public function testQuandoUmUsuarioEstaAptoASeLogar() { $user

    = $this->getMock( "MeuUsuarioDuble", array("isValid") ); $user->method("isValid")->willReturn(true); $authenticator = new Authenticator($user); $this->assertEquals( [ "role" => "default", "expire" => 3600 ], $authenticator->login("123") ); } } 24
  17. class AuthenticatorTest extends PHPUnit_Framework_TestCase { public function testQuandoUmUsuarioEstaAptoASeLogar() { $user

    = $this->getMock( "MeuUsuarioDuble", array("isValid") ); $user->method("isValid")->willReturn(true); $authenticator = new Authenticator($user); $this->assertEquals( [ "role" => "default", "expire" => 3600 ], $authenticator->login("123") ); } } Dublê (stub) 25
  18. Eles não são parte da interface pública da sua classe-alvo,

    sendo seu comportamento preservado. A melhor forma de testar um método privado é através de um método público. Stubbing 28
  19. Stubbing 30 • O método privado é "código morto". •

    O método não deveria ser privado. • Há um design smell na classe que está testando.
  20. Objetos que possuem implementações, mas, em geral, são incompletos ou

    limitados e não são utilizáveis em produção. Faking 33
  21. Objetos que possuem implementações, mas, em geral, são incompletos ou

    limitados e não são utilizáveis em produção. Faking 34
  22. Faking 35 Objetos que possuem implementações, mas, em geral, são

    incompletos ou limitados e não são utilizáveis em produção.
  23. 37

  24. class osC_Database { // 270 linhas } class osC_Database_Result {

    // 630 linhas } class osC_Database_mysql { // 300 linhas } class osC_Database_mysqli { // 200 linhas } class osC_Database_mysql_innodb { // 30 linhas } 39
  25. Com essa técnica, podemos simular a integração com o banco

    de dados e garantir o teste de uma determinada unidade. Faking 43
  26. 44 class osC_Database { private $_db; private $_query; public function

    connect() { $this->_db = new SQLite3(":memory:"); } public function query($query) { $this->_query = $this->_db->query($query); } public function execute() { return $this->_query->fetchArray(); } }
  27. require_once "database_sqlite.php"; class MeuModelTest extends PHPUnit_Framework_TestCase { public function testDeveInserirUmRegistro()

    { $model = new MeuModel(); $this->assertTrue($model->insert(["John Doe"])); } public function testDeveLancarUmaExcecao() { $model = new MeuModel(); $this->setExpectedException("Exception"); $this->assertEquals( $this->getExpectedException(), $model->insert([""]) ); } } 45
  28. 47 Criar objetos que simulam um comportamento pode ser arriscado,

    pois seus testes passam a depender da implementação em questão. Faking
  29. Objeto pré-definido com expectativas de que aquilo que você definiu

    irá de fato acontecer. Caso contrário, será um caso de falha. Mocking 50
  30. Objeto pré-definido com expectativas de que aquilo que você definiu

    irá de fato acontecer. Caso contrário, será um caso de falha. Mocking 51
  31. 52 class HttpRequest { private $_url; public function __construct($url) {

    $this->_url = $url; } public function execute() { $curl = curl_init(); ... return $response; } }
  32. 53 class HttpRequestTest extends PHPUnit_Framework_TestCase { public function testDeveRetornarUmaRespostaDaRequisicao() {

    $request = $this->getMock( "HttpRequest", array("execute"), array("http://null.com") ); $request->expects($this->once()) ->method("execute") ->will($this->equalTo("{}")); $this->assertEquals("{}", $request->execute()); } }
  33. 54 class HttpRequestTest extends PHPUnit_Framework_TestCase { public function testDeveRetornarUmaRespostaDaRequisicao() {

    $request = $this->getMock( "HttpRequest", array("execute"), array("http://null.com") ); $request->expects($this->once()) ->method("execute") ->will($this->equalTo("{}")); $this->assertEquals("{}", $request->execute()); } }
  34. 57 O problema da busca pela cobertura de código é

    que constantemente testamos o próprio mock. Mocking
  35. 58 Teste um método que dependa do método que foi

    "mockado". Ter 100% de cobertura do mock não o ajudará muito. Mocking
  36. 59 Além disso, há quem defenda o "only mock what

    you own". Ou seja, não mock seu framework, API e etc. Mocking
  37. 60 Mock apenas o que faz parte do seu domínio,

    considerando que seu framework, API e etc. é um domínio transversal. Mocking
  38. 61 Mock apenas o que faz parte do seu domínio,

    considerando que seu framework, API e etc. é um domínio transversal. Mocking Domínio que o ajuda a criar seu próprio domínio
  39. 62 Defina um número máximo de mocks que seu teste

    pode possuir. Caso exceda esse número, repense o design da classe-alvo. Mocking
  40. 65 class MinhaClasseTest extends PHPUnit_Framework_TestCase { public function testVerificaPassagemDeMensagens() {

    $object = $this->getMock("MinhaClasse"); $object->expects($this->any()); $object->expects($this->never()); $object->expects($this->once()); $object->expects($this->atLeastOnce()); $object->expects($this->exactly($x)); $object->expects($this->at($x)); ... } }
  41. Recomendações gerais Idealmente, em um teste, simular um objeto é

    errado. Se seu teste conhece detalhes da implementação da classe- alvo, estamos quebrando o encapsulamento. 68
  42. Recomendações gerais Quando seu teste sabe que um método qualquer

    tem uma determinada implementação, mudanças na classe- alvo faz com que o teste se altere também, o que pode ser uma má ideia. 69
  43. Recomendações gerais Porém, dependendo do tipo de teste, isto normalmente

    é feito quando temos dependência externa (banco de dados, requisições, etc.). 70
  44. 72 Pense bem antes de adotar uma prática lida em

    blogs. Blogs são, muitas vezes, relatos de experiências cotidianas.
  45. Ser um bom programador não significa escrever código complicado todo

    o tempo, mas conseguir escrever código simples o tempo todo! 73